-
Notifications
You must be signed in to change notification settings - Fork 59
Add support for inserting multi-modal documents via the helper pane #893
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for inserting multi-modal documents via the helper pane #893
Conversation
WalkthroughAdds a Template editing mode and Markdown tooling; migrates expression token handling to a CodeMirror parsed-token/compound-token model with document-token support and new chip styles; removes legacy tokenized components; and threads InputMode/template interpolation through helper panes, forms, and visualizer pages. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ExpressionEditor
participant ExpandedEditor
participant TemplateMode
participant CodeMirror
participant HelperPane
User->>ExpressionEditor: choose TEMPLATE mode / edit
ExpressionEditor->>ExpandedEditor: open(mode="template", sanitizedExpression, rawExpression)
ExpandedEditor->>TemplateMode: render TemplateMode
TemplateMode->>CodeMirror: init ChipExpressionEditor (sanitized/raw)
CodeMirror-->>TemplateMode: EditorView ready (onEditorViewReady)
User->>TemplateMode: open HelperPane / select item
HelperPane->>HelperPane: compute completionContext (getCurrentNavigationPath)
HelperPane-->>TemplateMode: onChange(wrappedValue) [wrapInTemplateInterpolation if TEMPLATE]
TemplateMode->>CodeMirror: insert token / update tokenField
User->>TemplateMode: toggle preview
TemplateMode->>TemplateMode: transformExpressionToMarkdown(value, tokens)
TemplateMode->>User: render MarkdownPreview with <chip> tokens
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
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. Comment |
66dc60f to
a1b33f8
Compare
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx (1)
55-93: AlignVariableItem’sonItemSelecttype with actual handlers
VariableItemPropsdefinesonItemSelect: (value: string, item: CompletionItem) => void;andVariableItemcalls it asonItemSelect(item.label, item), but the localhandleItemSelectinVariablesis defined as(value: string) => { ... }. This mismatch can cause TypeScript issues now thatVariableItemand its props are exported and intended for reuse.A minimal, backward‑compatible fix is to make the second parameter optional:
-export type VariableItemProps = { - item: CompletionItem; - onItemSelect: (value: string, item: CompletionItem) => void; - onMoreIconClick: (value: string) => void; - hideArrow?: boolean; -} +export type VariableItemProps = { + item: CompletionItem; + onItemSelect: (value: string, item?: CompletionItem) => void; + onMoreIconClick: (value: string) => void; + hideArrow?: boolean; +}
VariableItemcan continue to callonItemSelect(item.label, item); existing handlers that only accept(value: string)will keep working because the extra argument is ignored, and the type now reflects that.workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionBaseComponent.tsx (1)
148-162: Potential runtime crash:targetLineRangeis optional but dereferenced unconditionally
ChipExpressionBaseComponentPropsdeclarestargetLineRangeas optional:targetLineRange?: LineRange;but
fetchUpdatedFilteredTokensuses it as if it’s always defined:const response = await expressionEditorRpcManager?.getExpressionTokens( value, props.fileName, props.targetLineRange.startLine );If any caller omits
targetLineRange, this will throw at runtime (Cannot read properties of undefined). Either the prop should be required, or the call-site must guard againstundefined.One local fix that keeps the prop optional:
const fetchUpdatedFilteredTokens = useCallback(async (value: string): Promise<number[]> => { setIsLoading(true); try { + if (!props.targetLineRange) { + setIsLoading(false); + return []; + } const response = await expressionEditorRpcManager?.getExpressionTokens( value, props.fileName, props.targetLineRange.startLine ); setIsLoading(false); return response || []; } catch (error) { setIsLoading(false); return []; } }, [expressionEditorRpcManager, props.targetLineRange, props.fileName]);(Dependencies adjusted accordingly.) If in practice
targetLineRangeis always present, consider making it non-optional in the prop type instead.
🧹 Nitpick comments (10)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (1)
32-66: Navigation path building is correct; consider de‑duplicating gettersThe updated
navigateToNextlogic withseparatorbuilds dotted paths correctly for both root and nested levels, andnavigateToBreadcrumbcleanly trims steps.getCurrentNavigationPathcurrently duplicatesgetCurrentPathlogic; you can avoid duplication by implementing one in terms of the other (or aliasing) to keep the source of truth in a single place.workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Inputs.tsx (1)
88-121: Navigation‑aware completion context and offsets look correctUsing
getCurrentNavigationPathto derivenavigationPathand thencompletionContext(navigationPath + '.'when nested, otherwisecurrentValue) matches the expected semantics for field navigation. The offset calculation (navigationPath ? completionContext.length : 0) correctly positions the cursor after the dot for nested paths, while still requesting root‑level completions with offset 0.You could optionally include
getCurrentNavigationPathin theuseMemodependency array for lint friendliness, though it’s stable in practice.workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx (1)
103-117: Navigation‑aware completion context for Variables matches Inputs behaviorThe
navigationPath+completionContextpattern mirrors Inputs: when navigating inside a variable, you buildnavigationPath + '.', otherwise you fall back tocurrentValue. This is consistent and should give predictable completion behavior for nested variable structures.You could simplify the
useMemoreturningpathtouseMemo(() => getCurrentNavigationPath(), [breadCrumbSteps])or even callgetCurrentNavigationPath()directly if you don’t need memoization.workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TokenizedExpression.tsx (1)
23-39: TokenType integration is correct; consider tightening typesSwitching to
TokenTypeandTOKEN_TYPE_INDEX_MAPingetTokenTypeFromIndex/getTokenChipaligns this module with the new token model and is functionally correct. A couple of small type cleanups would improve safety:
getTokenTypeFromIndexcan returnTokenTypeinstead ofstringto reflect the actual values:-export const getTokenTypeFromIndex = (index: number): string => { - return TOKEN_TYPE_INDEX_MAP[index] || TokenType.FUNCTION; -}; +export const getTokenTypeFromIndex = (index: number): TokenType => { + return TOKEN_TYPE_INDEX_MAP[index] || TokenType.FUNCTION; +};
The
typeparameter inTokenizedExpressionPropscallbacks (onChipClick,onChipFocus) is still typed asstringeven thoughgetTokenChipnow passes aTokenType. If callers already work with the enum, consider updating these totype: TokenTypefor better type checking.
documentTypeis currently passed asdocumentType={documentType as any}. If you have aDocumentTypetype in../types, wiring that through instead ofstring/anywould avoid accidental misuse.These are non-breaking at runtime but will make the API clearer for future changes.
Also applies to: 70-77
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipComponent.tsx (1)
35-46: Consider removing the unreachable default case.The default case in getDocumentIcon will never be reached due to TypeScript's exhaustive type checking on DocumentType. You can simplify this or add an assertion for extra safety.
Apply this diff for a cleaner implementation:
const getDocumentIcon = (documentType: DocumentType): React.ReactNode => { switch (documentType) { case 'ImageDocument': return <Icon name="bi-image" sx={{ width: 16, height: 16, fontSize: 16 }} />; case 'FileDocument': return <Icon name="bi-doc" sx={{ width: 16, height: 16, fontSize: 16 }} />; case 'AudioDocument': return <Icon name="bi-audio" sx={{ width: 16, height: 16, fontSize: 16 }} />; - default: - return <Icon name="bi-attach-file" sx={{ width: 16, height: 16, fontSize: 16 }} />; } };workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (2)
80-110: Completion retrieval effect: consider dependencies and stale closuresThe
useEffectthat callshandleRetrieveCompletionsdepends ontargetLineRange,breadCrumbSteps, andcompletionContext, but usesfield,triggerCharacters,handleRetrieveCompletions,filteredCompletions, andshowContentfrom the closure. If any of those can change without unmounting (e.g., different field, updated trigger characters, or a re-createdhandleRetrieveCompletions), the effect will keep using stale values.If that’s a realistic scenario, it’d be safer to include the relevant items in the dependency array (and gate re-fetching with a separate flag if needed) to avoid subtle bugs where the pane doesn’t refresh completions as expected.
216-256: URL input: consider minimal validation or feedback for invalid URLs
URLInputModalcurrently only checks that the URL is non-empty before wrapping it in a document expression:if (!url.trim()) { return; } const wrappedValue = wrapInDocumentType(documentType, `"${url.trim()}"`);If users paste clearly invalid values, they get no feedback and the expression will still be inserted. Not strictly wrong, but a simple validation (e.g., basic URL shape or requiring a scheme) and/or an inline error message would improve UX and help avoid obviously broken inputs.
If you prefer to keep the server as the only validator, even just disabling
Createfor inputs that don’t match a simple regex would be a low-cost safeguard.workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionBaseComponent.tsx (3)
95-146: Backtick sanitization works, butsanitizedExpressionprop is only used as a boolean flag
sanitizeExpressionModelcorrectly strips a single leading/trailing backtick from the first/last literal and drops now-empty literals, which achieves the goal of hiding wrapper backticks in the editor.However, the
sanitizedExpression?: (value: string) => string;prop is only used as a truthy check:if (!props.sanitizedExpression || model.length === 0) { return model; }The callback itself is never invoked in this component. If the intent is that the parent controls the actual string-level sanitization (e.g., mapping between stored/raw expressions and UI-friendly ones), you might want either:
- A clearer boolean prop name (e.g.,
hideWrapperBackticks?: boolean), or- Actually applying
props.sanitizedExpressiontoupdatedValuebefore callingprops.onChange, if the parent expects to receive a transformed expression.Right now the API shape suggests caller-provided sanitization logic, but the implementation only uses it as an on/off switch.
246-278: Merged-token handling: behavior looks correct, but worth verifying token-refresh assumptionsThe merged-token guard and fetch trigger logic look sensible:
hasMergedTokenschecksel.metadatato detect merged document/variable tokens.- Manual token updates via
updateTokensare skipped when merged tokens exist.- You then force a full token refresh by setting
fetchnewTokensRef.current = truewhenhasMergedTokensis true.This should help avoid corrupting merged document tokens while still keeping tokens in sync.
Given how central tokens are to chip rendering, it’s worth double-checking that downstream helpers (e.g.,
mergeDocumentTokensinutils.ts) aren’t relying on the pre-sanitized invariant of always having leading/trailing literal elements, sincesanitizeExpressionModelnow filters empty literals. If they do, they may also need a corresponding adjustment.
148-169: Loading skeleton: consider consolidatingisLoadingusage and guarding function-signature errors
isLoadingdrives theSkeletonLoaderoverlay and is toggled both infetchUpdatedFilteredTokensandgetFnSignature. That’s fine, but:
- If
props.extractArgsFromFunctionever throws or rejects,setIsLoading(false)ingetFnSignaturewon’t run and the skeleton may stay visible indefinitely.- Multiple overlapping calls (tokens + signature) will race on
isLoading, which is acceptable visually but something to be aware of.A small refinement would be to wrap
extractArgsFromFunctionin atry/finallysimilar tofetchUpdatedFilteredTokens:const getFnSignature = useCallback(async (value: string, cursorPosition: number) => { if (props.extractArgsFromFunction) { - setIsLoading(true); - const fnSignature = await props.extractArgsFromFunction(value, cursorPosition); - setIsLoading(false); + setIsLoading(true); + try { + const fnSignature = await props.extractArgsFromFunction(value, cursorPosition); + if (fnSignature) { + return fnSignature; + } + } finally { + setIsLoading(false); + } - if (fnSignature) { - return fnSignature - } } return undefined; }, [props.extractArgsFromFunction]);This keeps the skeleton from getting “stuck” in the face of unexpected errors.
Also applies to: 380-483, 622-623
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
workspaces/common-libs/font-wso2-vscode/src/icons/bi-attach-file.svgis excluded by!**/*.svgworkspaces/common-libs/font-wso2-vscode/src/icons/bi-audio.svgis excluded by!**/*.svgworkspaces/common-libs/font-wso2-vscode/src/icons/bi-image.svgis excluded by!**/*.svg
📒 Files selected for processing (25)
workspaces/ballerina/ballerina-side-panel/src/components/editors/EditorFactory.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/ExpandedEditor.tsx(3 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/ExpressionMode.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionBaseComponent.tsx(11 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/AutoExpandingEditableDiv.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipComponent.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TokenizedExpression.tsx(3 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/styles.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts(13 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/Context.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Configurables.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Inputs.tsx(3 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx(6 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx(2 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx(6 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/iconUtils.tsx(2 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/types.ts(2 hunks)workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-10T15:05:11.309Z
Learnt from: madushajg
Repo: wso2/vscode-extensions PR: 868
File: workspaces/bi/bi-extension/src/utils.ts:224-242
Timestamp: 2025-11-10T15:05:11.309Z
Learning: The workspaces/bi/bi-extension and workspaces/ballerina/ballerina-extension are separate VS Code extensions that are packaged and distributed independently, so they cannot share code via imports and must maintain their own implementations of common utilities.
Applied to files:
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx
🧬 Code graph analysis (10)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx (4)
workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (3)
SlidingPaneNavContainer(224-258)SlidingPane(125-155)SlidingPaneHeader(281-288)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/iconUtils.tsx (1)
getHelperPaneIcon(40-46)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx (1)
Documents(28-72)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (1)
DocumentConfig(68-307)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx (1)
workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (1)
SlidingPaneNavContainer(224-258)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipComponent.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1)
DocumentType(59-59)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/styles.tsx (4)
Chip(54-81)DocumentChip(83-110)DocumentChipIcon(112-118)DocumentChipText(120-128)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Inputs.tsx (3)
workspaces/common-libs/ui-toolkit/src/components/Codicon/Codicon.tsx (1)
Codicon(44-56)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (2)
useHelperPaneNavigation(26-67)BreadCrumbStep(21-24)workspaces/ballerina/ballerina-side-panel/src/components/editors/utils.ts (1)
getPropertyFromFormField(64-81)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (7)
workspaces/ballerina/bi-diagram/src/utils/types.ts (1)
LineRange(53-53)workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts (1)
ExpressionProperty(1077-1077)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (2)
useHelperPaneNavigation(26-67)BreadCrumbStep(21-24)workspaces/ballerina/ballerina-visualizer/src/Context.tsx (2)
useModalStack(189-189)POPUP_IDS(136-144)workspaces/ballerina/ballerina-side-panel/src/components/editors/utils.ts (1)
getPropertyFromFormField(64-81)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx (1)
VariableItem(62-93)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Components/BreadcrumbNavigation.tsx (1)
BreadcrumbNavigation(28-128)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TokenizedExpression.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
TOKEN_TYPE_INDEX_MAP(29-37)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipComponent.tsx (1)
ChipComponent(48-114)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionBaseComponent.tsx (4)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1)
ExpressionModel(67-79)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (3)
createExpressionModelFromTokens(381-499)getTextValueFromExpressionModel(181-189)updateTokens(1343-1382)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/constants.ts (1)
BACKSPACE_MARKER(33-33)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/styles.tsx (1)
SkeletonLoader(353-366)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx (5)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/types.ts (1)
shouldShowNavigationArrow(89-92)workspaces/common-libs/ui-toolkit/src/components/Codicon/Codicon.tsx (1)
Codicon(44-56)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Components/HelperPaneListItem.tsx (1)
HelperPaneListItem(77-124)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (2)
useHelperPaneNavigation(26-67)BreadCrumbStep(21-24)workspaces/ballerina/ballerina-side-panel/src/components/editors/utils.ts (1)
getPropertyFromFormField(64-81)
workspaces/ballerina/ballerina-side-panel/src/components/editors/EditorFactory.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx (1)
ContextAwareRawExpressionEditor(23-38)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (1)
ContextAwareExpressionEditor(293-306)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (2)
DocumentType(59-59)ExpressionModel(67-79)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TokenizedExpression.tsx (1)
getTokenTypeFromIndex(26-28)
🔇 Additional comments (28)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Configurables.tsx (1)
207-207: Navigation API simplification is correct and verified.The
navigateToBreadcrumbfunction signature accepts only thestepparameter (as confirmed in the hook implementation at line 41 ofuseHelperPaneNavigation.tsx). All usages across the Helper Pane components—Variables, Inputs, DocumentConfig, and Configurables—consistently pass only the step parameter. Breadcrumb navigation state updates are handled internally by the hook viasetBreadCrumbSteps, so no side effects are lost with this change.workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/AutoExpandingEditableDiv.tsx (1)
286-290: Layout change is intentional and correct — no issues found.The change to
maxHeight: 'none'in expanded mode is a well-designed layout pattern. The outer container (ChipEditorField) can grow unbounded while the inner editor content remains fixed atEXPANDED_EDITOR_HEIGHT(500px) with scrolling. This allows helper buttons, completions menu, and UI controls to render outside the fixed scrollable area. The DocumentChip components integrate properly as non-editable elements within the tokenized expression. The minimum height constraint ensures the outer container never collapses below 500px.workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (2)
33-43: Overflow hidden on SlidingWindowContainer looks safeSwitching to
overflow-x: hidden/overflow-y: hiddenat the pane container level is consistent with using inner scrollable sections (e.g.,ScrollableContainer) and avoids double scrollbars. As long as all scrollable content is inside those inner containers—as it is in the helper-pane views—this is fine.
298-298: Re‑exportinguseSlidingPaneis a useful API additionRe‑exporting
useSlidingPanefrom this module simplifies consumers’ imports and aligns the hook with the rest of the SlidingPane API.workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Inputs.tsx (3)
66-71: Codicon end action usage is fineRendering the navigation arrow via
Codicon name="chevron-right"remains functionally identical and keeps the intent clear.
124-139: Root vs nested filtering for inputs is clear and safeThe root‑level filter that restricts items to
kind === "variable"with"Parameter"in the description, and the nested filter that allows both fields and variables (excludingself), gives a sensible distinction between top‑level parameters and object members. The optional chaining onlabelDetails?.descriptionprevents runtime errors when metadata is missing.
152-164: Path construction and navigation wiring for Inputs are consistent
handleItemSelectcorrectly constructs a full dotted path fromnavigationPathplus the selected value before callingonChange, andhandleInputsMoreIconClick+handleBreadCrumbItemClickeddelegate tonavigateToNext/navigateToBreadcrumbusing the same navigationPath basis. This keeps completion navigation and inserted expressions in sync.workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx (3)
122-145: Completion retrieval and offset logic for Variables look soundUsing
completionContextto derive the trigger character and offset (navigationPath ? completionContext.length : 0) is consistent with the navigation model and parallels Inputs. The effect dependencies ontargetLineRange,breadCrumbSteps, andcompletionContextare enough to refresh completions when navigation or context changes.
172-183: Variable dropdown filtering withexcludedDescriptionsis clearFiltering out variables whose
labelDetails.descriptionincludes"Configurable","Parameter","Listener", or"Client"while still requiringkind === "field" || kind === "variable"and excludingselfgives a well-scoped set of user-facing variables. Optional chaining aroundlabelDetails?.descriptionavoids runtime errors if metadata is missing.
196-200: Full-path selection and navigation wiring for Variables are consistent
handleItemSelectcorrectly builds a full dotted path fromnavigationPathand passes it toonChange, and the “more” and breadcrumb handlers delegate tonavigateToNext(value, navigationPath)/navigateToBreadcrumb(step)respectively. This keeps navigation, selection, and the inserted expression aligned.Also applies to: 219-224, 231-236
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TokenizedExpression.tsx (1)
96-117: Renderable token filtering and document chip rendering look correctUsing
isRenderableTokento skipSTART_EVENT,END_EVENT,TYPE_CAST, andVALUEfrom chip rendering (falling back toTextElementfor those) matches the expectation that only semantic tokens (variables, properties, documents, etc.) become chips. Passingelement.metadata?.content || element.valueas the chip text andelement.metadata?.documentTypethrough togetTokenChipensures document tokens can render with the correct label and icon while still preserving raw text in the non-token branch.workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (2)
203-379: LGTM: Well-structured document token merging logic.The token merging implementation is well-organized with clear helper functions and proper validation of token sequences. The separation between variable and document token merging patterns is appropriate.
501-503: The review comment is based on a misunderstanding.The
getTokenTypeFromIndexat lines 501-503 inutils.tsis a separate, locally-scoped function from the exportedgetTokenTypeFromIndexinTokenizedExpression.tsx. These are two different functions serving different purposes, not inconsistent implementations of the same code. TheTokenType.VARIABLEdefault inutils.tsis appropriate for its tokenizer parsing context, while theTokenType.FUNCTIONdefault inTokenizedExpression.tsxis appropriate for its separate context.Likely an incorrect or invalid review comment.
workspaces/ballerina/ballerina-visualizer/src/Context.tsx (1)
143-143: LGTM: New popup ID for document URL functionality.The addition of
DOCUMENT_URLtoPOPUP_IDSfollows the established pattern and supports the new document-related UI flows introduced in this PR.workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (1)
158-158: LGTM: Prop forwarding to support expression sanitization.The
sanitizedExpressionprop is correctly forwarded toChipExpressionBaseComponent, enabling the expression sanitization feature for chip-based expression editing.workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/ExpandedEditor.tsx (1)
44-44: LGTM: Clean prop threading for expression sanitization.The
sanitizedExpressionprop is appropriately added to the expanded editor and forwarded only to the expression mode path, maintaining separation of concerns between different editor modes.Also applies to: 128-128, 185-185
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx (1)
29-33: LGTM: Explicit prop handling with proper override precedence.The reordering ensures that
targetLineRangeandhelperPaneZIndexare passed explicitly, while{...props}at the end allows for prop overrides. This provides better control over prop precedence and aligns with the expanded prop surface for expression editors.workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts (1)
55-56: LGTM: Type extension for expression sanitization.The addition of the optional
sanitizedExpressionprop toEditorModeExpressionPropsproperly extends the interface to support the new sanitization feature in expression mode.workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx (2)
24-25: LGTM: Documents integration follows established patterns.The Documents and DocumentConfig panes are properly integrated into the SlidingPane navigation structure, following the same pattern as Variables, Inputs, and other existing panes. The prop wiring is consistent and complete.
Also applies to: 453-474
41-41: AI_PROMPT_TYPE constant value is correct.Verification confirms that the
AI_PROMPT_TYPEconstant value"ai:Prompt"matches the Ballerina type constraint system. The string is used consistently in the codebase, including independent validation inEditorFactory.tsxwherefield.valueTypeConstraint === "ai:Prompt"appears. The constant is properly used for conditional menu item rendering in the helper pane.workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/iconUtils.tsx (1)
28-28: LGTM!The DOCUMENT icon type addition is consistent with the existing pattern and properly maps to the "bi-attach-file" icon.
Also applies to: 37-37
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (1)
731-731: LGTM!Properly forwards sanitizedExpression to ExpandedEditor, enabling consistent expression sanitization in expanded mode.
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/ExpressionMode.tsx (1)
41-41: LGTM!Correctly threads sanitizedExpression through to ChipExpressionBaseComponent, maintaining the sanitization pipeline.
Also applies to: 56-56
workspaces/ballerina/ballerina-side-panel/src/components/editors/EditorFactory.tsx (1)
158-182: LGTM! Editor routing updated for document support.The editor routing logic has been updated to use ContextAwareRawExpressionEditor for RAW_TEMPLATE or ai:Prompt fields, with enhanced context props (openSubPanel, recordTypeField, etc.) enabling richer document editing experiences.
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1)
29-40: LGTM!The TokenType enum, DocumentType, and TokenMetadata types provide proper type safety for the document token system. The optional metadata field correctly handles both document and non-document tokens.
Also applies to: 59-65, 74-78
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/styles.tsx (1)
83-128: LGTM!The DocumentChip styled components provide clear visual distinction for document tokens with appropriate theming and layout.
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipComponent.tsx (1)
69-94: LGTM!The document chip rendering logic properly handles TokenType.DOCUMENT with icon and text, maintaining consistency with existing variable and property chip patterns.
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionBaseComponent.tsx (1)
347-354: Expanded token fetch triggers (comma, newline, paste) look goodThe updated
shouldFetchNewTokenslogic to include commas, newlines, and multi-character “paste-like” input as triggers is a good addition for keeping tokens and completions consistent with user edits:const endsWithTriggerChar = lastTypedText.endsWith('+') || lastTypedText.endsWith(' ') || lastTypedText.endsWith(',') || lastTypedText.endsWith('\n'); // Detect paste const isPaste = lastTypedText.length > 1 && !isSpecialKey; return endsWithTriggerChar || isSpecialKey || isPaste;This is a nice usability improvement and aligns with expected editor behavior.
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx
Outdated
Show resolved
Hide resolved
82e9f0b to
bff07a2
Compare
415ad73 to
d3341bc
Compare
d339dce to
094fda6
Compare
65a9328 to
12b4080
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/MarkdownPreview.tsx (1)
19-27: Fix plugin configuration error and implement proper content sanitization for user-supplied markdown.Two critical issues found:
Plugin Configuration Error (line 61):
remarkGfmis incorrectly placed inrehypePluginsarray. Remark plugins must be inremarkPlugins. This likely breaks GitHub Flavored Markdown support.XSS Vulnerability: User-controlled markdown from
PromptMode.tsx(line 172:content={value}) and template expressions fromTemplateMode.tsx(line 132:content={transformedContent}) are rendered withrehypeRawenabled. ThedisallowedElementsapproach is insufficient—it blocks only specific tags but cannot prevent XSS via event handlers on allowed elements (<img onerror="...">), SVG-based attacks, or style expressions.Move
remarkGfmto a newremarkPluginsprop and either:
- Remove
rehypeRawand use markdown-safe rendering only, or- Sanitize content with a proper HTML sanitizer (e.g.,
DOMPurify) before passing toMarkdownPreview
🧹 Nitpick comments (7)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (1)
70-103: Harden preview fallback and optional onTogglePreview handling.Two small robustness points:
- Preview when tokens / rpcManager aren’t available
transformContentonly updatestransformedContentwhenisPreviewMode && value && expressionEditorRpcManagerare all truthy. If preview is toggled on beforeexpressionEditorRpcManageris ready (or if it’s ever null),MarkdownPreviewwill render with the initial empty string. It would be safer to always fall back to the sanitized (or raw) value whenever tokens aren’t available, e.g.:if (!expressionEditorRpcManager) { const displayValue = sanitizedExpression ? sanitizedExpression(value) : value; setTransformedContent(displayValue); return; }
- onTogglePreview is optional
onTogglePreviewis typed as optional, but inside the toolbar props you call it unguarded:onTogglePreview={() => onTogglePreview(!isPreviewMode)}Given it’s optional, it’s cleaner (and more TypeScript‑friendly) to either:
- Guard directly on
onTogglePreview:{onTogglePreview && ( <TemplateMarkdownToolbar ... onTogglePreview={() => onTogglePreview(!isPreviewMode)} /> )}
- Or use optional chaining in the callback:
onTogglePreview={() => onTogglePreview?.(!isPreviewMode)}The outer
hasPreviewSupportcheck already prevents runtime issues, but tightening this up avoids relying on implicit assumptions about the prop always being passed.Also applies to: 120-160
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (1)
33-42: Document helper pane behavior for TEMPLATE vs expression looks correctThe document helper correctly:
- Narrows completions to compatible types per
documentType.- Distinguishes AI document variables vs values needing wrapping.
- Uses
InputMode.TEMPLATEto decide whether to wrap in interpolation (${…}) or insert plain expressions, both for variables and the “Add from URL” flow.The control flow (breadcrumb navigation, completion context, and minimal loader) also looks sound; no functional problems stand out.
Also applies to: 44-50, 52-92, 96-134, 147-179, 189-201, 207-218, 248-297
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/chipStyles.ts (1)
19-37: Chip styling & icon helpers are consistent with the new token modelThe chip styles and helpers (
TOKEN_TYPE_COLORS, icon maps,getTokenIconClass,getChipDisplayContent) align well with the newTokenType/DocumentTypesetup and should give clear visual differentiation for variables, parameters, properties, and documents. You might optionally tighten the typing ofgetTokenIconClass’ssubTypetoDocumentTypefor extra safety, but functionally this looks good.Also applies to: 39-49, 50-77, 79-90, 92-101, 103-109, 111-127
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (2)
384-406: Verify use ofrawExpressionwhen computingfieldValuefor diagnosticsLine 384 computes:
const fieldValue = inputModeRef.current === InputMode.TEMPLATE && rawExpression ? rawExpression(watch(key)) : watch(key);If the form value (
watch(key)) is already the raw expression (as implied by lateronChange(rawValue)paths and howTemplateModecalls the language server), callingrawExpressionon it again is likely backwards:rawExpressionis intended to map from sanitized → raw, not raw → raw.This could skew the string that diagnostics run against in TEMPLATE mode. It might be safer to:
- Use
watch(key)directly for diagnostics, or- If the stored value is actually sanitized, use
rawExpression(watch(key))consistently in all LS calls (includingTemplateMode).Worth double-checking the intended contract of
sanitizedExpression/rawExpressionand ensuring they’re only applied in one direction.
498-516: Template mode plumbing and helper-pane integration look good overallThe use of
inputModeRef.currentwhen:
- Passing the current mode into
getHelperPane(line 498-516),- Translating editor values with
rawExpressionin theExpressionFieldandExpandedEditoronChangehandlers (lines 670-701, 760-795),- Restricting diagnostics to EXP/TEMPLATE,
is consistent and should avoid stale-closure issues in async callbacks. Once the small issues above are addressed, the TEMPLATE mode flow here looks structurally sound.
Also applies to: 670-701, 750-810
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
20-41: Token parsing & compound detection are well-structured; be mindful of mapping/regex assumptionsThe token utilities (
ParsedToken,getParsedExpressionTokens,TOKEN_PATTERNS,detectTokenPatterns, and the document/variable metadata extractors) are nicely factored and line up with howCodeUtils.tsconsumes them for chips and compound sequences.Two assumptions to keep in mind:
mapRawToSanitized/mapSanitizedToRawrely onsanitizedExpressionbeing a contiguous substring of the raw expression. If sanitization ever rewrites or reorders content more aggressively (e.g., stripping tags from the middle), these mappings may become inaccurate.extractDocumentMetadatauses/\$\{<ai:(\w+)>\{content:\s*([^}]+)\}\}/, so document content can’t contain}and must follow that exact pattern. That matches whatwrapInDocumentTypecurrently generates, but could miss more complex document expressions.Given current usage, this is acceptable; just worth revisiting if sanitization or document syntax evolves.
Also applies to: 94-101, 103-137, 145-156, 158-199, 201-257, 261-274, 276-331
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (1)
21-35: Chip widgets, token state, and Backspace behavior look robustThe refactor to:
- Build chips via
createChipwith sharedBASE_*styles and token-type colors/icons,- Track
{ tokens, compounds }intokenFieldand recompute compounds on token changes/removals,- Use
iterateTokenStreamto decorate both compound and standalone tokens, and- Make Backspace delete entire compound sequences when the cursor is inside them,
is coherent and should give a much better UX for document/variable chips without breaking existing tokens. No functional problems stand out here.
Also applies to: 55-71, 84-117, 197-258, 261-322, 324-371, 378-419, 507-515
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
common/config/rush/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlworkspaces/common-libs/font-wso2-vscode/src/icons/bi-attach-file.svgis excluded by!**/*.svgworkspaces/common-libs/font-wso2-vscode/src/icons/bi-audio.svgis excluded by!**/*.svgworkspaces/common-libs/font-wso2-vscode/src/icons/bi-image.svgis excluded by!**/*.svg
📒 Files selected for processing (45)
workspaces/ballerina/ballerina-side-panel/package.json(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/Form/types.ts(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/ModeSwitcher/styles.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/EditorFactory.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/ExpandedEditor.tsx(8 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/ChipComponent.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/MarkdownPreview.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/TemplateMarkdownToolbar.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/ExpressionMode.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts(3 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/styles/markdown-preview.css(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/templateUtils.ts(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/transformToMarkdown.ts(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx(15 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx(3 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CHipTest.tsx(0 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts(8 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/chipStyles.ts(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/AutoExpandingEditableDiv.tsx(0 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipComponent.tsx(0 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx(16 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/HelperPaneToggleButton.tsx(4 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TextElement.tsx(0 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TokenizedExpression.tsx(0 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/styles.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts(3 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts(1 hunks)workspaces/ballerina/ballerina-visualizer/src/Context.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx(3 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGeneratorNew/index.tsx(3 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Configurables.tsx(8 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Inputs.tsx(5 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx(8 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx(2 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx(10 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/iconUtils.tsx(2 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/types.ts(2 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts(1 hunks)workspaces/common-libs/ui-toolkit/src/components/Commons/ErrorBanner.tsx(2 hunks)workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx(2 hunks)
💤 Files with no reviewable changes (5)
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipComponent.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TextElement.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CHipTest.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TokenizedExpression.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/AutoExpandingEditableDiv.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
- workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/iconUtils.tsx
- workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx
- workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Configurables.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/HelperPaneToggleButton.tsx
- workspaces/ballerina/ballerina-visualizer/src/Context.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/EditorFactory.tsx
- workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGeneratorNew/index.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/styles.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/ModeSwitcher/styles.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-10T15:05:11.309Z
Learnt from: madushajg
Repo: wso2/vscode-extensions PR: 868
File: workspaces/bi/bi-extension/src/utils.ts:224-242
Timestamp: 2025-11-10T15:05:11.309Z
Learning: The workspaces/bi/bi-extension and workspaces/ballerina/ballerina-extension are separate VS Code extensions that are packaged and distributed independently, so they cannot share code via imports and must maintain their own implementations of common utilities.
Applied to files:
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx
🧬 Code graph analysis (21)
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (2)
ContextAwareExpressionEditorProps(49-65)ExpressionEditor(306-825)workspaces/ballerina/ballerina-side-panel/src/context/form.tsx (1)
useFormContext(70-70)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/ChipComponent.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1)
DocumentType(61-61)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/chipStyles.ts (5)
getTokenIconClass(92-97)StandardChip(113-120)StandardIcon(123-125)ChipText(127-127)getChipDisplayContent(107-109)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/TemplateMarkdownToolbar.tsx (4)
workspaces/common-libs/ui-toolkit/src/styles/Theme.ts (1)
ThemeColors(79-104)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/templateUtils.ts (7)
insertMarkdownFormatting(24-74)insertMarkdownLink(103-154)insertMarkdownHeader(79-98)insertMarkdownBlockquote(159-192)insertMarkdownUnorderedList(251-257)insertMarkdownOrderedList(259-265)insertMarkdownTaskList(267-273)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/HelperPaneToggleButton.tsx (1)
HelperPaneToggleButton(81-104)workspaces/common-libs/ui-toolkit/src/components/Swich/Swich.tsx (1)
Switch(146-176)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/transformToMarkdown.ts (3)
workspaces/ballerina/ballerina-extension/src/features/tracing/webview.ts (1)
escapeHtml(266-275)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (2)
getParsedExpressionTokens(111-137)detectTokenPatterns(277-331)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (1)
iterateTokenStream(261-322)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/MarkdownPreview.tsx (3)
workspaces/common-libs/ui-toolkit/src/styles/Theme.ts (1)
ThemeColors(79-104)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/ChipComponent.tsx (1)
ChipComponent(36-44)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1)
DocumentType(61-61)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (8)
workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts (1)
ExpressionProperty(1077-1077)workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
InputMode(31-31)workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (2)
useSlidingPane(298-298)ScrollableContainer(182-186)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (2)
useHelperPaneNavigation(26-64)BreadCrumbStep(21-24)workspaces/ballerina/ballerina-visualizer/src/Context.tsx (2)
useModalStack(189-189)POPUP_IDS(136-144)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx (1)
VariableItem(64-95)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Components/BreadcrumbNavigation.tsx (1)
BreadcrumbNavigation(28-128)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Components/EmptyItemsPlaceHolder.tsx (1)
EmptyItemsPlaceHolder(23-35)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/ExpressionMode.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (1)
ChipExpressionEditorComponent(96-453)workspaces/common-libs/ui-toolkit/src/components/Commons/ErrorBanner.tsx (1)
ErrorBanner(46-55)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
InputMode(31-31)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx (3)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
InputMode(31-31)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (2)
useHelperPaneNavigation(26-64)BreadCrumbStep(21-24)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1)
wrapInTemplateInterpolation(22-24)
workspaces/ballerina/ballerina-side-panel/src/components/Form/types.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
InputMode(31-31)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/chipStyles.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1)
DocumentType(61-61)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (4)
TokenMetadata(63-67)DocumentType(61-61)TokenPattern(100-105)CompoundTokenSequence(89-97)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/ExpandedEditor.tsx (3)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/ExpressionMode.tsx (1)
ExpressionMode(36-78)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (1)
TemplateMode(39-162)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts (1)
EditorMode(86-86)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (3)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (5)
buildOnFocusListner(427-458)CursorInfo(44-48)buildOnSelectionChange(462-495)expressionEditorKeymap(378-423)tokensChangeEffect(197-197)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
mapSanitizedToRaw(183-199)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/templateUtils.ts (1)
listContinuationKeymap(348-353)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx (5)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
InputMode(31-31)workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (3)
SlidingPaneNavContainer(224-258)SlidingPane(125-155)SlidingPaneHeader(281-288)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/iconUtils.tsx (1)
getHelperPaneIcon(40-46)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx (1)
Documents(28-72)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (1)
DocumentConfig(52-299)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx (1)
workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (1)
SlidingPaneNavContainer(224-258)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Inputs.tsx (3)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
InputMode(31-31)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (2)
useHelperPaneNavigation(26-64)BreadCrumbStep(21-24)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1)
wrapInTemplateInterpolation(22-24)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (7)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts (1)
EditorModeExpressionProps(50-81)workspaces/ballerina/ballerina-side-panel/src/context/form.tsx (1)
useFormContext(70-70)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/transformToMarkdown.ts (1)
transformExpressionToMarkdown(45-106)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/TemplateMarkdownToolbar.tsx (1)
TemplateMarkdownToolbar(105-198)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/MarkdownPreview.tsx (1)
MarkdownPreview(56-86)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (1)
ChipExpressionEditorComponent(96-453)workspaces/common-libs/ui-toolkit/src/components/Commons/ErrorBanner.tsx (1)
ErrorBanner(46-55)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (3)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (2)
TokenMetadata(63-67)CompoundTokenSequence(89-97)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/chipStyles.ts (6)
getChipDisplayContent(107-109)getTokenTypeColor(99-101)BASE_CHIP_STYLES(22-37)getTokenIconClass(92-97)BASE_ICON_STYLES(39-42)CHIP_TEXT_STYLES(44-48)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (4)
ParsedToken(104-109)getParsedExpressionTokens(111-137)mapRawToSanitized(159-180)detectTokenPatterns(277-331)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (1)
workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/utils/token.ts (1)
setValue(103-145)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
InputMode(31-31)
🪛 Biome (2.1.2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/styles/markdown-preview.css
[error] 232-232: Duplicate font names are redundant and unnecessary: Mono
Remove duplicate font names within the property
(lint/suspicious/noDuplicateFontNames)
[error] 321-321: Duplicate font names are redundant and unnecessary: Mono
Remove duplicate font names within the property
(lint/suspicious/noDuplicateFontNames)
[error] 328-328: Duplicate font names are redundant and unnecessary: Mono
Remove duplicate font names within the property
(lint/suspicious/noDuplicateFontNames)
🔇 Additional comments (46)
workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (2)
297-298: LGTM! Clean API expansion.Re-exporting
useSlidingPanemakes the hook conveniently available to external consumers of this module.
38-39: No issues found. Overflow change correctly implements fixed-viewport pattern.The change from
overflow-x: scrolltohiddenis intentional and correct. TheSlidingWindowContaineracts as a fixed-size viewport (320px × max 350px), while scrolling responsibility is delegated to child components—specifically theScrollableContainercomponent (which hasoverflow: auto). All actual content throughout the codebase is properly wrapped inScrollableContaineror similar scrollable children, ensuring no content becomes inaccessible. This is standard viewport architecture with no double-scrollbar issues.workspaces/ballerina/ballerina-side-panel/package.json (1)
31-32: LGTM! Markdown dependencies added appropriately.The addition of
rehype-rawandremark-gfmaligns with the markdown rendering enhancements in the ExpandedEditor/MarkdownPreview components.workspaces/common-libs/ui-toolkit/src/components/Commons/ErrorBanner.tsx (2)
23-30: LGTM! Style customization support added correctly.The optional
sxprop is properly typed and threaded through to the Container's inline styles, enabling flexible style overrides while maintaining backward compatibility.
46-50: LGTM! Public API extended appropriately.The ErrorBanner now accepts and forwards the
sxprop, maintaining backward compatibility while enabling style customization.workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/styles/markdown-preview.css (1)
1-580: LGTM! Comprehensive markdown stylesheet added.This CSS provides extensive, well-structured styling for markdown content including typography, accessibility features, and interactive elements. The implementation follows standard markdown rendering patterns.
Note: The Biome lint warnings about "duplicate font names" at lines 232, 321, and 328 are false positives. The font stacks correctly specify
"Liberation Mono"(a specific font) followed bymonospace(a generic font family fallback), which is standard practice.workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (2)
32-39: LGTM! Navigation logic improved.The separator logic correctly prevents a leading dot when
currentValueis empty, and the refactored internal state management simplifies the API by removing the externalonChangecallback.
54-62: LGTM! Public API extended appropriately.The new
getCurrentNavigationPathexport provides a clear, purpose-specific alias for accessing the navigation path, improving API clarity for consumers.workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (8)
83-94: LGTM! Public API extended to support sanitization and template modes.The new props enable sanitization-aware editing, helper pane state exposure, and template-specific features like list continuation. The type signatures are well-defined and backward-compatible.
106-106: LGTM! Selection tracking added.The
savedSelectionRefenables cursor position preservation across helper pane interactions, ensuring smooth user experience when inserting completions.
155-202: LGTM! Sanitization-aware completion handling implemented correctly.The logic properly:
- Uses saved selection when available
- Converts between sanitized and raw expressions for API calls via
mapSanitizedToRaw- Preserves cursor position for function argument templates
- Clears saved selection after use to prevent stale state
241-249: LGTM! Helper pane state exposed to parent.The
onHelperPaneStateChangecallback enables parent components to coordinate with the helper pane state, supporting advanced UI interactions.
257-261: LGTM! List continuation conditionally enabled.The
listContinuationKeymapis only included whenenableListContinuationis true, providing opt-in support for markdown list editing.
317-336: LGTM! Sanitization integrated into token updates.The editor correctly:
- Applies
sanitizedExpressionto the displayed value- Passes both
rawValueandsanitizedValueto token effects- Triggers external updates only when sanitized value differs from current doc
357-359: LGTM! Token refresh triggered on sanitization mode changes.Scheduling a token update when sanitization props change ensures tokens are re-rendered appropriately when switching between expression and template modes.
381-388: LGTM! Escape key handling added.The Escape key handler properly closes the helper pane and prevents event propagation, improving keyboard navigation.
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/templateUtils.ts (4)
24-74: LGTM! Markdown formatting toggle implemented correctly.The function properly handles both wrapping and unwrapping scenarios, checking both the selection itself and the surrounding text. The cursor positioning logic maintains a good user experience.
103-154: LGTM! Link formatting with smart toggling.The link insertion logic:
- Detects and unwraps existing
[text](url)patterns- Handles surrounding link context with heuristics
- Selects the URL portion for easy editing when creating new links
202-249: LGTM! Robust list toggling implementation.The
toggleListhelper correctly:
- Handles both single-line and multi-line selections
- Preserves indentation when adding/removing list markers
- Maintains selection range for toggle operations
- Moves cursor to end for typing convenience
300-346: LGTM! List continuation logic well-designed.The
handleEnterForListContinuationfunction:
- Detects task lists, unordered lists, and ordered lists correctly
- Exits lists on empty items (deletes the marker)
- Increments ordered list numbers appropriately
- Splits lines at cursor position for partial-line edits
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/types.ts (2)
38-86: LGTM! Comprehensive primitive type detection.The
isPrimitiveTypefunction correctly handles:
- Direct primitive types (string, int, boolean, etc.)
- Arrays of primitives (
string[])- Maps with primitive values (
map<string>)- Union types including optional types (
string|int,int?)The improved spacing enhances readability.
89-91: LGTM! Navigation arrow logic refined.The function correctly shows navigation arrows for non-primitive types and Records, enabling drill-down navigation in the helper pane.
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1)
22-24: LGTM! Template interpolation wrapper implemented correctly.The function properly wraps values in
${}syntax when in template mode, supporting the template-aware expression insertion in helper panes.workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
31-31: LGTM!Clean addition to the module's public API. The export follows the established pattern and makes InputMode accessible to consumers without requiring deep imports.
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/TemplateMarkdownToolbar.tsx (3)
1-32: LGTM!Imports are well-organized and all dependencies are necessary for the toolbar functionality.
34-103: LGTM!The styled components and interface are well-structured with proper theme integration, accessibility support (focus-visible states), and comprehensive interaction states (hover, active, disabled).
105-200: LGTM!The component implementation follows React best practices with proper use of forwardRef, displayName, and event handling. The mouseDown prevention pattern (line 112-114) correctly maintains editor focus while allowing toolbar interactions.
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx (4)
23-37: LGTM!The template configuration correctly handles different value type constraints with appropriate prefix/suffix patterns for
ai:Promptandstringtypes.
39-45: LGTM!The function handles various input types correctly with appropriate fallbacks for undefined constraints and array types.
73-84: LGTM!The component correctly computes the template configuration and passes the helper functions to ExpressionEditor. The prop spreading order ensures explicit props take precedence appropriately.
51-71: ****The template wrapping logic is correct and appropriately designed for Ballerina template expressions. The functions handle the intended use case properly:
getSanitizedExpcorrectly unwraps properly formatted templates (e.g., "stringhello" → "hello")getRawExpcorrectly wraps unwrapped expressions- The symmetrical logic prevents double-wrapping
The edge case concern (coincidental prefix/suffix matching) is negligible given the specific template syntax patterns ("string
" and "") and the context of template expression editing rather than arbitrary string processing. No actual implementation issues found.workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/ChipComponent.tsx (1)
1-44: LGTM!Clean, focused component with proper TypeScript typing. The integration with utility functions (
getTokenIconClass,getChipDisplayContent) and styled components follows the established patterns in the codebase.workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (2)
44-44: LGTM!The addition of the
rawExpressionprop is consistent with the existingsanitizedExpressionpattern and properly typed.
126-162: LGTM!The
rawExpressionprop is correctly forwarded toChipExpressionEditorComponentalongsidesanitizedExpression, maintaining symmetry in the template expression handling flow.workspaces/ballerina/ballerina-side-panel/src/components/Form/types.ts (2)
23-23: LGTM!Import is necessary for the type signature extension and follows standard import patterns.
188-189: LGTM!The optional
inputModeparameter extends the helper pane API to support mode-aware behavior while maintaining backward compatibility.workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx (2)
22-26: LGTM!The
AIDocumentTypeenum values correctly use theai:namespace prefix for Ballerina document types. The previous enum value mismatch has been properly addressed.
28-72: LGTM!The component provides a clean, consistent interface for selecting document types. The three items follow a uniform pattern with appropriate icons and navigation wiring.
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/MarkdownPreview.tsx (2)
29-46: LGTM!The styling updates properly integrate with the imported
markdown-preview.cssand use theme colors for consistency.
56-86: LGTM with security measures in place.The implementation includes appropriate security controls:
- Dangerous elements (script, iframe, object, embed) are disabled/disallowed
- Custom chip renderer safely extracts node properties with optional chaining
The custom chip component (lines 67-76) handles token rendering with sensible defaults (
TokenType.VARIABLEfallback on line 71).workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx (1)
39-42: inputMode and Documents wiring looks consistent.The new
inputMode?: InputModeprop is correctly threaded fromHelperPaneNewPropsintoHelperPaneNewEl, passed down toVariables,Inputs,Configurables, andDocumentConfig, and forwarded viagetHelperPaneNew. The Documents and DocumentConfig panes are also correctly registered in the sliding window and gated onforcedValueTypeConstraint?.includes(AI_PROMPT_TYPE), which lines up with the AI prompt use case.Also applies to: 72-73, 98-100, 370-371, 387-388, 429-431, 433-455, 531-532
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/ExpressionMode.tsx (1)
23-24: Expression mode wiring for sanitization and errors looks good.Passing
sanitizedExpression,rawExpression, andgetHelperPanethrough toChipExpressionEditorComponentmatches the multi-mode editor design, and the conditionalErrorBannerrendering based onerrorvsformDiagnosticskeeps error display centralized without changing editor behavior.Also applies to: 42-48, 55-75
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Inputs.tsx (1)
24-25: Template-aware input insertion and navigation look correct.Using
useHelperPaneNavigation("Inputs")to derivenavigationPathand then:
- Building
completionContextfromnavigationPathvscurrentValue,- Computing
offsetfromcompletionContext.length, and- Wrapping the final
fullPathwithwrapInTemplateInterpolation(fullPath, inputMode)gives the expected behavior for both expression and template modes while keeping the breadcrumb navigation coherent. The updated completion fetch and filtering logic still respects the root/parameter vs nested-field distinction.
Also applies to: 34-43, 85-100, 112-118, 154-160
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts (1)
22-24: Extended EditorModeExpressionProps and EditorMode union align with new modes.Adding
sanitizedExpression,rawExpression, optional preview controls, anderror/formDiagnosticstoEditorModeExpressionProps, plus extendingEditorModewith"template", matches how ExpressionMode and TemplateMode are now wired. The surface area looks consistent with the multi-mode editor and form‑diagnostics needs.Also applies to: 50-81, 86-86
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1)
19-29: Token/type model for TEMPLATE & documents looks coherentThe new
InputMode.TEMPLATE,TokenTypeenum,DocumentType,TokenMetadata,CompoundTokenSequence, andTokenPatterndefinitions are internally consistent and line up with how they’re consumed inchipStyles.ts,utils.ts, and CodeMirror utilities. Only thing to double‑check is thatDocumentTypestring literals ("ImageDocument" | "FileDocument" | "AudioDocument") stay in sync with the values emitted in document expressions and inAIDocumentTypeto avoid icon/metadata mismatches.Also applies to: 31-42, 61-68, 69-81, 88-105
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/ExpandedEditor.tsx (1)
28-34: Expanded editor TEMPLATE mode wiring looks solidTemplate mode is cleanly integrated into
MODE_COMPONENTS, props (includingsanitizedExpression,rawExpression,getHelperPane,error,formDiagnostics) are correctly scoped to expression/template modes, and themodestate reset ondefaultModechange avoids stale mode when switching fields. No functional issues spotted here.Also applies to: 35-64, 107-113, 148-153, 155-172, 180-187, 215-250
...rina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/transformToMarkdown.ts
Show resolved
Hide resolved
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx
Show resolved
Hide resolved
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx
Show resolved
Hide resolved
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx
Show resolved
Hide resolved
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx
Show resolved
Hide resolved
…for Record types
…les in interpolations
…ed on AI prompt type
…proved compound handling
12b4080 to
458b5b3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (1)
32-45: Breadcrumb navigation should not rely solely on label matching
navigateToBreadcrumblocates the step bylabel:const index = breadCrumbSteps.findIndex(item => item.label === step.label);If different levels share the same label (e.g. nested records with the same field name), clicking a deeper breadcrumb will jump to the first occurrence instead of the intended one.
Since you already have
replaceTextand the caller passes the fullBreadCrumbStep, it would be more robust to match on something unique (e.g.replaceTextor the array index passed from the UI) rather than justlabel.Also applies to: 54-55, 57-63
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (1)
55-125: Ensure ChipWidget.eq accounts for type/metadata to avoid stale chip rendering
ChipWidget.eqonly comparestext,start, andend. Now that styling and content can depend ontypeandmetadata(e.g.,documentTypeaffecting icon/color andmetadata.contentfor display), a change in metadata with identical text and range will not cause the widget to re-render, leaving the chip visually out of sync.Consider including
typeand at least the relevant metadata fields in the equality check:- eq(other: ChipWidget) { - return other.text === this.text && other.start === this.start && other.end === this.end; - } + eq(other: ChipWidget) { + return ( + other.text === this.text && + other.start === this.start && + other.end === this.end && + other.type === this.type && + JSON.stringify(other.metadata) === JSON.stringify(this.metadata) + ); + }You can optimize the metadata comparison if you know exactly which fields drive rendering.
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/MarkdownPreview.tsx (1)
23-83: WireremarkGfmunderremarkPlugins, notrehypePlugins, to enable GFM correctlyThe
remark-gfmplugin is a remark plugin that should be passed via ReactMarkdown'sremarkPluginsprop, while raw HTML is enabled viarehype-rawinrehypePlugins. Currently,remarkGfmis incorrectly passed inrehypePlugins, which prevents GitHub-Flavored Markdown features (tables, task lists, etc.) from being applied correctly and may cause incompatibilities with the rehype stage.- <div className="markdown-body"> - <ReactMarkdown - rehypePlugins={[rehypeRaw, remarkGfm]} + <div className="markdown-body"> + <ReactMarkdown + remarkPlugins={[remarkGfm]} + rehypePlugins={[rehypeRaw]} components={{ // Prevent rendering of potentially dangerous elementsworkspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx (1)
271-346: Hard-coded menu item ref indices are fragile.The menu items use sparse, hard-coded indices (0, 2, 1, 3, 4, 5) for
menuItemRefs.current[index]. If the conditional rendering logic changes (e.g., "Create Value" visibility), these indices could point to the wrong elements or become invalid, breaking keyboard navigation and focus management.Consider deriving indices dynamically based on the actual render order to keep the ref array aligned with the visual order.
♻️ Duplicate comments (2)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx (1)
102-105: Keyboard navigation count can desync from visible menu items.This issue was already flagged in a previous review. The
currentMenuItemCountis calculated based on constraints, but the actual number of rendered menu items depends on runtime conditions (e.g.,valueCreationOptions.length > 0for "Create Value",forcedValueTypeConstraint?.includes(AI_PROMPT_TYPE)for "Documents"). This can cause keyboard navigation to skip or miss rendered items.Also applies to: 271-287, 289-346
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (1)
567-575: The field reset issue flagged in previous reviews is still present.Lines 571-572 call
setValue(key, rawExpression("")), which clears the field value when confirming an EXP→TEMPLATE mode switch. The past review comment indicated this was "Addressed in commits 3c35b23 to 458b5b3", but the code remains unchanged.This should either:
- Be removed entirely if no transformation is needed: just switch the mode
- Transform the current value:
setValue(key, rawExpression(watch(key)))Clearing the field on mode switch is unexpected UX and discards user input.
🧹 Nitpick comments (10)
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx (2)
73-84: Consider reordering props for clearer precedence.The explicit props at lines 75-77 are placed before the spreads, meaning they can be overridden by
...form,...expressionEditor, or...props. WhileExpressionEditorlikely handles this with fallback logic, placing explicit props after spreads would make the intended precedence more obvious and improve maintainability.Apply this diff to clarify precedence:
return ( <ExpressionEditor - fileName={fileName} - targetLineRange={targetLineRange} - helperPaneZIndex={props.helperPaneZIndex} {...form} {...expressionEditor} {...props} + fileName={fileName} + targetLineRange={targetLineRange} + helperPaneZIndex={props.helperPaneZIndex} rawExpression={getRawExp} sanitizedExpression={getSanitizedExp} /> );
49-71: Consider memoizing helper functions to prevent unnecessary re-renders.The
templateConfig,getSanitizedExp, andgetRawExpare recreated on every render. IfExpressionEditoror its children use these in dependency arrays, it could trigger unnecessary re-renders. Consider memoizing them for better performance.Apply this diff to memoize the values:
+import React, { useMemo, useCallback } from "react"; -import React from "react"; import { useFormContext } from "../../context"; import { ContextAwareExpressionEditorProps, ExpressionEditor } from "./ExpressionEditor"; export const ContextAwareRawExpressionEditor = (props: ContextAwareExpressionEditorProps) => { const { form, expressionEditor, targetLineRange, fileName } = useFormContext(); - const templateConfig = getTemplateConfig(props.field.valueTypeConstraint); + const templateConfig = useMemo( + () => getTemplateConfig(props.field.valueTypeConstraint), + [props.field.valueTypeConstraint] + ); - const getSanitizedExp = (value: string) => { + const getSanitizedExp = useCallback((value: string) => { if (!value) { return value; } const { prefix, suffix } = templateConfig; if (value.startsWith(prefix) && value.endsWith(suffix)) { return value.slice(prefix.length, -suffix.length); } return value; - }; + }, [templateConfig]); - const getRawExp = (value: string) => { + const getRawExp = useCallback((value: string) => { if (!value) { return value; } const { prefix, suffix } = templateConfig; if (!value.startsWith(prefix) && !value.endsWith(suffix)) { return `${prefix}${value}${suffix}`; } return value; - }; + }, [templateConfig]);workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (1)
64-69: Hook dependencies rely on state props; verify against your lint rulesBoth
navigationPathand theuseEffectfor completions key offbreadCrumbSteps/completionContextbut omit several referenced values (e.g.,getCurrentNavigationPath,handleRetrieveCompletions,field,triggerCharacters,filteredCompletions,showContent) from their dependency arrays.Behavior‑wise this is probably fine given these values are stable in practice, but if you have
react-hooks/exhaustive-depsenabled you may get warnings. If that’s the case, either:
- Add the missing deps and rely on memoized callbacks/contexts, or
- Explicitly disable the rule for these hooks with a comment and a short justification.
Also applies to: 71-94
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
139-156: Completion filtering behavior changed; validate UX impact
filterCompletionsByPrefixAndTypenow:
- For empty prefix: returns only
kind === 'field'.- For non‑empty prefix: returns
function | variable | fieldwhose label starts with the prefix.This is a reasonable heuristic, but it does change the surface of what users see initially (no functions/variables until they start typing). It’s worth double‑checking this against expected UX and any existing tests to ensure it matches the intended discovery behavior for the helper pane.
If you’d like, I can sketch a small set of unit tests around this helper to lock in the desired filtering semantics.
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (1)
261-371: iterateTokenStream + chipPlugin decoration pipeline is well-structured; consider tightening typesThe new
iterateTokenStreamabstraction correctly:
- Emits compounds first (grouped by
startIndex).- Skips tokens consumed by compounds.
- Skips START/END event tokens and validates ranges against
docLength.
chipPlugin.buildDecorationsusing this iterator andcreateChipwith metadata is a nice separation of concerns. The only minor nit is typingwidgetsasany[]; you can make this more explicit without changing behavior:- const widgets: any[] = []; // Type as any[] to allow pushing Range<Decoration> + const widgets: Range<Decoration>[] = [];This keeps the API surface cleaner for future refactors.
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/TemplateMarkdownToolbar.tsx (1)
34-198: Toolbar wiring to markdown insert utilities and preview toggle looks correctThe toolbar correctly:
- Routes actions to
insertMarkdown*helpers with the providededitorView.- Keeps editor focus by preventing default on
onMouseDown.- Disables formatting actions and the helper-pane toggle when
isPreviewModeis true.- Integrates cleanly with the
Switchfor Edit/Preview.Given the helpers already no-op on
nullviews, the behavior is safe as-is.workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/styles/markdown-preview.css (1)
229-337: Address Biome duplicate-font-name warnings in monospace stacksStatic analysis flags duplicate font names (e.g.,
Mono) in the monospace font stacks used forkbdand code-related elements. While behavior is fine at runtime, these warnings may fail CI.You can either:
- Slightly simplify the stacks (e.g., drop one of the overlapping
*Monoentries), or- Suppress this specific lint rule for these declarations if you prefer to keep the GitHub-equivalent stack verbatim.
No functional impact, but worth resolving for a clean lint run.
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/templateUtils.ts (1)
103-154: Link insertion logic works but has a minor inconsistency.The link unwrapping/wrapping logic is functional, but line 132 has a comment noting "−1 to keep the ')' logic aligned", which suggests the calculation might not be entirely correct.
Consider verifying the surrounding link unwrapping logic more carefully:
{ from: to, to: to + urlLen - 1, insert: '' } // Line 132Should this be
to + urlLenwithout the-1? TheurlMatch[0]includes the full](url)pattern, so the length should be correct without adjustment.workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/styles.tsx (2)
83-110: Refactor to reduce duplication and use theme colors.The
DocumentChipcomponent shares most of its styling with theChipcomponent (lines 54-81), differing only in colors and border. Additionally, the hardcodedrgba(59, 130, 246, ...)values bypass the theme system.Consider refactoring to:
- Extract shared styles into a base component or use props to customize colors
- Replace hardcoded rgba values with
ThemeColorsconstants or CSS variables for theme consistencyExample refactor using a prop-based approach:
const BaseChip = styled.div<{ variant?: 'default' | 'document' }>` border-radius: 4px; background-color: ${props => props.variant === 'document' ? 'rgba(59, 130, 246, 0.15)' : 'rgba(0, 122, 204, 0.3)' }; color: var(--vscode-input-foreground, ${props => props.variant === 'document' ? 'inherit' : 'white'}); ${props => props.variant === 'document' && 'border: 1px solid rgba(59, 130, 246, 0.4);'} cursor: pointer; margin: 2px 0px; font-size: 12px; padding: 2px 10px; display: inline-flex; justify-content: center; align-items: center; min-height: 20px; min-width: 25px; transition: all 0.2s ease; outline: none; vertical-align: middle; user-select: none; -webkit-user-select: none; &:hover { background-color: ${props => props.variant === 'document' ? 'rgba(59, 130, 246, 0.25)' : 'rgba(0, 122, 204, 0.5)' }; } &:active { box-shadow: ${props => props.variant === 'document' ? '0 0 0 2px rgba(59, 130, 246, 0.8)' : '0 0 0 2px rgba(0, 122, 204, 0.8)' }; ${props => props.variant === 'default' && `background-color: ${ThemeColors.PRIMARY};`} } `; export const Chip = styled(BaseChip)``; export const DocumentChip = styled(BaseChip).attrs({ variant: 'document' })``;Then update the hardcoded colors to use
ThemeColorsor add new theme color constants.
112-118: Consider using theme colors for consistency.The icon color uses the same hardcoded
rgba(59, 130, 246, 0.9)asDocumentChip. For better theme support, consider using a theme color constant.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
common/config/rush/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlworkspaces/common-libs/font-wso2-vscode/src/icons/bi-attach-file.svgis excluded by!**/*.svgworkspaces/common-libs/font-wso2-vscode/src/icons/bi-audio.svgis excluded by!**/*.svgworkspaces/common-libs/font-wso2-vscode/src/icons/bi-image.svgis excluded by!**/*.svg
📒 Files selected for processing (47)
workspaces/ballerina/ballerina-extension/grammar/ballerina-grammar(1 hunks)workspaces/ballerina/ballerina-side-panel/package.json(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/Form/types.ts(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/ModeSwitcher/styles.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/EditorFactory.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/ExpandedEditor.tsx(8 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/ChipComponent.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/MarkdownPreview.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/TemplateMarkdownToolbar.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/ExpressionMode.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts(3 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/styles/markdown-preview.css(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/templateUtils.ts(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/transformToMarkdown.ts(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx(15 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx(3 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CHipTest.tsx(0 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts(8 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/chipStyles.ts(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/AutoExpandingEditableDiv.tsx(0 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipComponent.tsx(0 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx(16 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/HelperPaneToggleButton.tsx(4 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TextElement.tsx(0 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TokenizedExpression.tsx(0 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/styles.tsx(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts(2 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts(3 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx(1 hunks)workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts(1 hunks)workspaces/ballerina/ballerina-visualizer/src/Context.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx(3 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGeneratorNew/index.tsx(3 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Configurables.tsx(8 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx(1 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Functions.tsx(5 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Inputs.tsx(5 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx(8 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx(2 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx(11 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/iconUtils.tsx(2 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/types.ts(2 hunks)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts(1 hunks)workspaces/common-libs/ui-toolkit/src/components/Commons/ErrorBanner.tsx(2 hunks)workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx(2 hunks)
💤 Files with no reviewable changes (5)
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TextElement.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CHipTest.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/AutoExpandingEditableDiv.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipComponent.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/TokenizedExpression.tsx
✅ Files skipped from review due to trivial changes (1)
- workspaces/ballerina/ballerina-extension/grammar/ballerina-grammar
🚧 Files skipped from review as they are similar to previous changes (20)
- workspaces/ballerina/ballerina-side-panel/src/components/Form/types.ts
- workspaces/ballerina/ballerina-visualizer/src/Context.tsx
- workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/iconUtils.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/EditorFactory.tsx
- workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/types.ts
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/HelperPaneToggleButton.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/ChipComponent.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/transformToMarkdown.ts
- workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGenerator/index.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts
- workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/chipStyles.ts
- workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
- workspaces/common-libs/ui-toolkit/src/components/Commons/ErrorBanner.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts
- workspaces/ballerina/ballerina-side-panel/package.json
- workspaces/ballerina/ballerina-visualizer/src/views/BI/Forms/FormGeneratorNew/index.tsx
- workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Configurables.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/ExpressionMode.tsx
- workspaces/ballerina/ballerina-side-panel/src/components/ModeSwitcher/styles.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-10T15:05:11.309Z
Learnt from: madushajg
Repo: wso2/vscode-extensions PR: 868
File: workspaces/bi/bi-extension/src/utils.ts:224-242
Timestamp: 2025-11-10T15:05:11.309Z
Learning: The workspaces/bi/bi-extension and workspaces/ballerina/ballerina-extension are separate VS Code extensions that are packaged and distributed independently, so they cannot share code via imports and must maintain their own implementations of common utilities.
Applied to files:
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx
🧬 Code graph analysis (15)
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (2)
ContextAwareExpressionEditorProps(49-65)ExpressionEditor(306-826)workspaces/ballerina/ballerina-side-panel/src/context/form.tsx (1)
useFormContext(70-70)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
InputMode(31-31)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/index.ts (1)
EditorMode(20-20)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/MarkdownPreview.tsx (3)
workspaces/common-libs/ui-toolkit/src/styles/Theme.ts (1)
ThemeColors(79-104)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/ChipComponent.tsx (1)
ChipComponent(36-44)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1)
DocumentType(61-61)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx (1)
workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (1)
SlidingPaneNavContainer(224-258)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx (4)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
InputMode(31-31)workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (3)
SlidingPaneNavContainer(224-258)SlidingPane(125-155)SlidingPaneHeader(281-288)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx (1)
Documents(28-72)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (1)
DocumentConfig(52-299)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (5)
buildOnFocusListner(427-458)CursorInfo(44-48)buildOnSelectionChange(462-495)expressionEditorKeymap(378-423)tokensChangeEffect(197-197)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/templateUtils.ts (1)
listContinuationKeymap(348-353)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/TemplateMarkdownToolbar.tsx (4)
workspaces/common-libs/ui-toolkit/src/styles/Theme.ts (1)
ThemeColors(79-104)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/templateUtils.ts (7)
insertMarkdownFormatting(24-74)insertMarkdownLink(103-154)insertMarkdownHeader(79-98)insertMarkdownBlockquote(159-192)insertMarkdownUnorderedList(251-257)insertMarkdownOrderedList(259-265)insertMarkdownTaskList(267-273)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/HelperPaneToggleButton.tsx (1)
HelperPaneToggleButton(81-104)workspaces/common-libs/ui-toolkit/src/components/Swich/Swich.tsx (1)
Switch(146-176)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (4)
TokenMetadata(63-67)DocumentType(61-61)TokenPattern(100-105)CompoundTokenSequence(89-97)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Inputs.tsx (2)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (2)
useHelperPaneNavigation(26-64)BreadCrumbStep(21-24)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1)
wrapInTemplateInterpolation(22-24)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (7)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts (1)
EditorModeExpressionProps(50-81)workspaces/ballerina/ballerina-side-panel/src/context/form.tsx (1)
useFormContext(70-70)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/transformToMarkdown.ts (1)
transformExpressionToMarkdown(45-106)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/TemplateMarkdownToolbar.tsx (1)
TemplateMarkdownToolbar(105-198)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/MarkdownPreview.tsx (1)
MarkdownPreview(56-86)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (1)
ChipExpressionEditorComponent(96-463)workspaces/common-libs/ui-toolkit/src/components/Commons/ErrorBanner.tsx (1)
ErrorBanner(46-55)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Functions.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
InputMode(31-31)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1)
wrapInTemplateInterpolation(22-24)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (7)
workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts (1)
ExpressionProperty(1077-1077)workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (1)
useSlidingPane(298-298)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/hooks/useHelperPaneNavigation.tsx (2)
useHelperPaneNavigation(26-64)BreadCrumbStep(21-24)workspaces/ballerina/ballerina-visualizer/src/Context.tsx (2)
useModalStack(189-189)POPUP_IDS(136-144)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Variables.tsx (1)
VariableItem(64-95)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Components/BreadcrumbNavigation.tsx (1)
BreadcrumbNavigation(28-128)workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Components/EmptyItemsPlaceHolder.tsx (1)
EmptyItemsPlaceHolder(23-35)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/ExpandedEditor.tsx (3)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/ExpressionMode.tsx (1)
ExpressionMode(36-78)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (1)
TemplateMode(39-162)workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts (1)
EditorMode(86-86)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (3)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (2)
TokenMetadata(63-67)CompoundTokenSequence(89-97)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/chipStyles.ts (6)
getChipDisplayContent(107-109)getTokenTypeColor(99-101)BASE_CHIP_STYLES(22-37)getTokenIconClass(92-97)BASE_ICON_STYLES(39-42)CHIP_TEXT_STYLES(44-48)workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (4)
ParsedToken(104-109)getParsedExpressionTokens(111-137)mapRawToSanitized(159-180)detectTokenPatterns(277-331)
🪛 Biome (2.1.2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/styles/markdown-preview.css
[error] 232-232: Duplicate font names are redundant and unnecessary: Mono
Remove duplicate font names within the property
(lint/suspicious/noDuplicateFontNames)
[error] 321-321: Duplicate font names are redundant and unnecessary: Mono
Remove duplicate font names within the property
(lint/suspicious/noDuplicateFontNames)
[error] 328-328: Duplicate font names are redundant and unnecessary: Mono
Remove duplicate font names within the property
(lint/suspicious/noDuplicateFontNames)
🔇 Additional comments (30)
workspaces/common-libs/ui-toolkit/src/components/ExpressionEditor/components/Common/SlidingPane/index.tsx (2)
33-43: Verify scroll behavior after changing SlidingWindowContainer tooverflow-y: hidden.With
max-height: 350pxandoverflow-y: hidden, any pane content that isn’t wrapped inScrollableContainer(or another scrollable child) will now be clipped instead of scrolling on the outer container. Please double‑check existing SlidingPane usages to ensure long content is always inside a scrollable child so we don’t regress accessibility/scroll behavior in older panes.
297-298: Re‑exportinguseSlidingPanelooks good.Re‑exporting the hook from this index module is consistent with how the rest of the API is exposed and should simplify consumers of the helper pane without changing semantics.
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx (2)
23-37: LGTM! Clear template configuration structure.The
TemplateConfiginterface andTEMPLATE_CONFIGSmapping provide a clean way to define prefix/suffix rules for different type constraints. The distinction betweenai:Prompt(backticks only) andstring(with "string " prefix) is appropriate.
39-45: LGTM! Consider union type scenarios.The function correctly resolves template configuration. Line 43 takes the first element when the constraint is an array, which is reasonable for most cases. If union types with conflicting template requirements become a concern, this logic may need refinement.
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1)
19-24: Template interpolation wrapper looks goodThe helper cleanly centralizes
${...}wrapping for template mode and keeps behavior transparent for other modes. No issues from a correctness or API‑shape perspective.workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
28-40: Parsed token construction matches semantic token encodingThe combination of
TOKEN_TYPE_INDEX_MAP,getTokenChunks, andgetParsedExpressionTokenscorrectly applies the(deltaLine, deltaStartChar)semantics from semantic tokens to reconstruct absolute[start, end)ranges and map numeric types toTokenType.This provides a solid base for the downstream pattern detection and metadata extraction.
Also applies to: 103-137
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (4)
197-259: TokenFieldState update and tokensChangeEffect/compounds recomputation look solidThe
TokenFieldStatestructure andtokenField.updatelogic (mapping positions throughtr.changes, parsing tokens from the payload, mapping raw→sanitized positions, and recomputing compounds on bothtokensChangeEffectandremoveChipEffect) are consistent and defensive. Recomputing compounds once per token change/removal keeps decoration logic simple and predictable.No issues from a correctness standpoint here.
379-419: Backspace compound deletion behavior is consistent and safeThe Backspace handler correctly:
- Deletes entire compound sequences when the cursor is inside a compound (dispatching multiple
removeChipEffects and clearing the underlying range).- Falls back to per-token deletion outside compounds.
- Returns
falsewhen no token/compound is affected, allowing the default keymap to handle standard text deletion.The cursor range checks and use of
tokenField(state, false)look good; no functional issues spotted.
507-515: Undo/redo-triggered token refetch is a useful additionThe new
undo/redobranch inbuildNeedTokenRefetchListnerensures tokens are re-fetched whenever the document changes via history operations, not just direct input. That closes a common desync gap between doc text and token metadata.Implementation is straightforward and fits well with the existing listener.
609-619: Helper pane Escape key handling remains correct with explicit EditorView typingTyping the
_viewparameter asEditorViewinbuildHelperPaneKeymapis a small but helpful clarity improvement. The logic (closing the helper pane only when open, otherwise delegating) is unchanged and correct.No action needed.
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx (1)
22-71: Documents view and AIDocumentType enum are consistent and well-structuredThe
AIDocumentTypeenum values align with the AI document type strings, and eachSlidingPaneNavContainercorrectly passes the corresponding type intoDOCUMENT_CONFIG. Labels and icons are clear for File/Image/Audio entries.No issues spotted here.
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Functions.tsx (1)
20-20: LGTM: Clean template mode integration for function insertions.The inputMode prop is properly threaded through and used to wrap function insertion values in template interpolation syntax (
${...}) when in template mode. This implementation is consistent with the pattern used in other helper pane views.Also applies to: 39-39, 50-50, 62-62, 181-182
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/index.tsx (1)
24-25: LGTM: Documents support and inputMode threading implemented correctly.The Documents and DocumentConfig panes are properly wired into the sliding pane system, conditionally rendered when
AI_PROMPT_TYPEis inforcedValueTypeConstraint, and theinputModeprop is correctly threaded through all child components (Variables, Inputs, Functions, Configurables, DocumentConfig).Also applies to: 39-39, 72-72, 334-346, 434-456, 532-532
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/types.ts (1)
22-23: LGTM: Clean type extensions for template mode and preview support.The type additions properly extend
EditorModeExpressionPropswith preview-related props (isPreviewMode,onTogglePreview), expression transformation functions (sanitizedExpression,rawExpression), and diagnostics (error,formDiagnostics). All new props are optional, maintaining backward compatibility. The addition of"template"to theEditorModeunion is straightforward.Also applies to: 49-81, 86-86
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (1)
39-162: Well-structured template mode implementation with preview support.The component cleanly separates editing mode (using
ChipExpressionEditorComponent) from preview mode (usingMarkdownPreview), with proper toolbar integration for markdown formatting. The token-based transformation to markdown and error banner rendering are well implemented.workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Inputs.tsx (1)
24-24: LGTM: Navigation-based context and template mode integration are correct.The refactor to use
getCurrentNavigationPath()for building completion context is sound. The offset logic correctly usescompletionContext.lengthwhen navigating into objects and0at the root. ThehandleItemSelectproperly builds the full path fromnavigationPathand wraps it in template interpolation wheninputModeisTEMPLATE. This aligns with the broader template mode integration across helper pane views.Also applies to: 29-29, 42-42, 86-86, 90-90, 94-123, 154-168
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/ExpandedEditor.tsx (1)
184-186: LGTM!The effect correctly resets the mode when the
defaultModeprop changes, ensuring the local state stays synchronized with parent-driven mode changes.workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (3)
345-360: LGTM! Good use of ref to avoid stale closures.Using
inputModeRefand keeping it synchronized with theinputModestate correctly prevents stale closure issues in callbacks likeonChangeandgetExpressionEditorDiagnostics, where the mode needs to be checked at the time of execution rather than at the time of callback creation.
524-525: LGTM! Guards against runtime errors.Normalizing
watch(key)before callingtrim()correctly prevents runtime errors when the field value isundefinedor not a string, addressing the concern from previous reviews.
432-440: Verify the template mode initialization logic for edge cases.The condition on line 435 checks
sanitized !== initialFieldValue.current || !initialFieldValue.current || initialFieldValue.current.trim() === '', which decides between TEMPLATE and EXP modes.Consider the edge case where
initialFieldValue.currentis a non-empty string butsanitizedis an empty string (e.g., a value that gets completely sanitized away). This would trigger TEMPLATE mode, which might be correct, but verify this is the intended behavior.workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (5)
106-225: LGTM! Well-implemented cursor preservation pattern.The
savedSelectionRefcorrectly preserves the user's cursor position across helper pane interactions. The implementation:
- Saves selection on relevant events (change, focus, selection change)
- Falls back to current selection if saved is
null- Clears after use to avoid stale state
This ensures a smooth UX when inserting items from the helper pane.
167-194: Function argument extraction logic handles template interpolation correctly.The enhanced logic correctly handles both plain function calls (
func()) and string template interpolations (${func()}). The regex pattern and cursor position adjustments properly extract the function definition for argument retrieval.The comment on line 162-166 correctly notes this is a workaround until the API provides proper completion templates with
$1,$2placeholders.
267-271: LGTM! Conditional keymap inclusion.Conditionally merging the
listContinuationKeymapbased on theenableListContinuationprop correctly enables markdown list continuation only when template mode requires it, avoiding unintended behavior in other editor contexts.
391-398: LGTM! Standard escape key handling.The Escape key handler correctly closes the helper pane with proper event handling (
preventDefaultandstopPropagation) to prevent interference with other handlers.
327-343: LGTM! Proper sanitization handling for display and tokenization.The code correctly:
- Uses
sanitizedExpressionto compute the display value (line 327)- Passes both
rawValueandsanitizedValuetotokensChangeEffect(lines 339-343)This separation enables template mode to render chips while preserving the underlying raw expression value.
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/templateUtils.ts (3)
24-74: LGTM! Well-structured markdown formatting toggle.The
insertMarkdownFormattingfunction correctly:
- Detects and unwraps internally formatted text
- Detects and unwraps surrounding formatting
- Wraps unformatted selections
- Preserves selection/cursor position throughout
The logic handles edge cases properly and maintains good UX.
202-249: LGTM! Robust generic list toggle implementation.The
toggleListfunction correctly handles:
- Both multi-line selections and single-line/cursor cases
- Indentation preservation
- Toggling all lines uniformly (on or off)
- Selection preservation for multi-line edits vs. cursor positioning for typing
The
hasSelectioncheck (line 209) provides good UX by preserving the selection range when toggling existing lists but moving the cursor to the end when creating a new list item.
300-346: LGTM! Proper list continuation on Enter.The
handleEnterForListContinuationfunction implements standard markdown editor behavior:
- Exits the list when Enter is pressed on an empty list item (lines 318-324)
- Continues the list with the next marker when pressed on non-empty items (lines 327-342)
- Handles cursor position correctly when splitting lines mid-content
This provides a smooth markdown editing experience.
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/styles.tsx (2)
54-81: LGTM! Improved chip centering and visual styling.The changes to use
inline-flexwithjustify-contentandalign-itemscenter improve the chip alignment, and the reduced background opacity (0.3) provides better visual hierarchy.
120-128: LGTM! Proper text overflow handling.The component correctly implements text truncation with ellipsis, which is appropriate for chip-based UI elements with limited space.
...aces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/ExpandedEditor.tsx
Show resolved
Hide resolved
.../ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx
Show resolved
Hide resolved
.../ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx
Show resolved
Hide resolved
...na-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts
Show resolved
Hide resolved
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx
Show resolved
Hide resolved
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx
Show resolved
Hide resolved
There was a problem hiding this 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)
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx (2)
39-45: Handle arrayvalueTypeConstraintmore robustly ingetTemplateConfigWhen
valueTypeConstraintis an array, always taking index 0 can ignore later entries that actually have a template config (e.g., unions where the first element is unsupported but a later one is"ai:Prompt"or"string"). Consider iterating and returning the first constraint that exists inTEMPLATE_CONFIGS, falling back only if none match.-const getTemplateConfig = (valueTypeConstraint?: string | string[]): TemplateConfig => { - if (!valueTypeConstraint) { - return { prefix: "`", suffix: "`" }; - } - const constraint = Array.isArray(valueTypeConstraint) ? valueTypeConstraint[0] : valueTypeConstraint; - return TEMPLATE_CONFIGS[constraint] || { prefix: "`", suffix: "`" }; -}; +const DEFAULT_TEMPLATE_CONFIG: TemplateConfig = { prefix: "`", suffix: "`" }; + +const getTemplateConfig = (valueTypeConstraint?: string | string[]): TemplateConfig => { + if (!valueTypeConstraint) { + return DEFAULT_TEMPLATE_CONFIG; + } + const constraints = Array.isArray(valueTypeConstraint) + ? valueTypeConstraint + : [valueTypeConstraint]; + + for (const c of constraints) { + const config = TEMPLATE_CONFIGS[c]; + if (config) { + return config; + } + } + + return DEFAULT_TEMPLATE_CONFIG; +};
73-83: Minor JSX cleanup and optional callback memoization
helperPaneZIndexis passed explicitly and then again via{...props}, so the explicit prop is redundant and can be dropped. Also, ifExpressionEditorever depends on stable function identities forrawExpression/sanitizedExpression(e.g., inuseEffectdeps or memoization), consider wrappinggetRawExp/getSanitizedExpinuseCallback; otherwise the current inline definitions are fine.- <ExpressionEditor - fileName={fileName} - targetLineRange={targetLineRange} - helperPaneZIndex={props.helperPaneZIndex} - {...form} - {...expressionEditor} - {...props} - rawExpression={getRawExp} - sanitizedExpression={getSanitizedExp} - /> + <ExpressionEditor + fileName={fileName} + targetLineRange={targetLineRange} + {...form} + {...expressionEditor} + {...props} + rawExpression={getRawExp} + sanitizedExpression={getSanitizedExp} + />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (2)
ContextAwareExpressionEditorProps(49-65)ExpressionEditor(306-826)workspaces/ballerina/ballerina-side-panel/src/context/form.tsx (1)
useFormContext(70-70)
🔇 Additional comments (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/RawExpressionEditor.tsx (2)
23-37: TemplateConfig mapping looks correct for current template typesThe
TemplateConfigshape andTEMPLATE_CONFIGSentries for"ai:Prompt"and"string"align with the expected template syntaxes and are clear to extend later.
51-71: Sanitized/raw expression helpers are now symmetric and safe against double‑wrapping
getSanitizedExpandgetRawExpcorrectly mirror each other for the configuredprefix/suffix, only wrapping when both are missing and stripping when both are present, which avoids double-wrapping and keeps partially wrapped values unchanged (which seems like a sensible conservative behavior).
…sequence detection
There was a problem hiding this 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 (5)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (5)
20-40: Token type index mapping is clear; consider making unknown indices explicitThe semantic-token index constants and
TOKEN_TYPE_INDEX_MAPline up with the 5‑tuple chunk format and keep decoding readable. Right now any unmappedtypeindex falls back toTokenType.VARIABLE, which could silently misclassify tokens if the provider adds new token types.To make mismatches easier to spot, consider handling unknown indices explicitly (e.g., logging, throwing in dev, or mapping to a dedicated
UNKNOWNtoken type if you have one) instead of defaulting toVARIABLE. This will surface integration issues earlier if the semantic token legend drifts.Also applies to: 94-101
139-156: Completion filtering logic is sensible; just be aware of thekindwhitelist
getWordBeforeCursorPositionandfilterCompletionsByPrefixAndTypeare straightforward and read well. Note that:
- With an empty prefix you only surface items with
kind === 'field'.- With a non‑empty prefix you only surface items whose
kindis'function' | 'variable' | 'field'.If the toolkit ever introduces additional
CompletionItem.kindvalues you want to show (e.g., methods or properties under other labels), they will be silently filtered out. That’s fine if these are the only expected kinds, but worth keeping in mind.
158-199: Raw ↔ sanitized mapping assumes a single contiguous sanitized slice—document this invariant
mapRawToSanitized/mapSanitizedToRawcorrectly handle the “sanitized is a contiguous substring of raw” case by mapping:
- any position before the sanitized slice to
0,- positions inside to a relative offset,
- positions after to the end of the sanitized string.
This matches the typical “trim wrapper noise” scenario. The main hidden assumption is that
sanitizedExpressionoccurs exactly once and is the only region you care about. If that invariant ever changes (e.g., multiple identical snippets, or sanitized text built from disjoint parts), these helpers will start to behave unexpectedly.I’d at least document this invariant in a short comment/JSDoc, and optionally add a unit test that pins the current behavior for prefix/suffix positions so future refactors don’t accidentally change it.
201-257: Metadata extractors work; consider tightening regexes and validating document typesBoth
extractDocumentMetadataandextractVariableMetadatacorrectly derivecontentandfullValuefrom the token span. A couple of small robustness tweaks could help:
- The regexes are currently unanchored (
/.../), so any extra characters infullValuewould still produce a match. Anchoring to the full span makes mis‑tokenization fail safe:- const documentRegex = /\$\{<ai:(\w+)>\{content:\s*([^}]+)\}\}/; + const documentRegex = /^\$\{<ai:(\w+)>\{content:\s*([^}]+)\}\}$/; ... - const variableRegex = /\$\{([^}]+)\}/; + const variableRegex = /^\$\{([^}]+)\}$/;
match[1]is cast toDocumentTypewithout runtime validation. If the expression grammar ever emits an unsupportedai:type, it will sneak through as an apparently validDocumentType. You could guard this with a small whitelist:+const SUPPORTED_DOCUMENT_TYPES: DocumentType[] = ['ImageDocument', 'FileDocument', 'AudioDocument']; ... - const documentType = match[1] as DocumentType; + const candidateType = match[1] as string; + if (!SUPPORTED_DOCUMENT_TYPES.includes(candidateType as DocumentType)) { + return null; + } + const documentType = candidateType as DocumentType;Not mandatory, but it will make failures in upstream parsing/tokenization much easier to diagnose.
277-331: Pattern detection algorithm is correct; minor micro‑optimization possibleThe sliding‑window matcher over
ParsedToken[]is clear and correctly:
- Sorts patterns by priority,
- Enforces non‑overlapping matches via
usedIndices,- Delegates to extractors and pushes well‑structured
CompoundTokenSequenceobjects.The only small nit is the allocation in the “already used?” check:
if (Array.from({ length: sequenceLength }, (_, j) => i + j).some(idx => usedIndices.has(idx))) { continue; }You can avoid creating a temporary array on every window with a simple loop:
- if (Array.from({ length: sequenceLength }, (_, j) => i + j).some(idx => usedIndices.has(idx))) { - continue; - } + let overlaps = false; + for (let j = 0; j < sequenceLength; j++) { + if (usedIndices.has(i + j)) { + overlaps = true; + break; + } + } + if (overlaps) { + continue; + }Not critical given typical token counts, but it tightens the inner loop a bit.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (4)
TokenMetadata(63-67)DocumentType(61-61)TokenPattern(100-105)CompoundTokenSequence(89-97)
🔇 Additional comments (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (2)
103-137: Semantic token → absolute ParsedToken conversion looks correctThe
getParsedExpressionTokensimplementation correctly applies LSP‑style deltas forlineandstartCharand then converts to absolute offsets viagetAbsoluteColumnOffset. Incrementing a separatetokenIdand normalizing the rawtypethroughgetTokenTypeFromIndexgives you a clean, stable token surface for the rest of the pipeline. No issues spotted here.
259-275: Token pattern priorities now match the comment and intent
TOKEN_PATTERNSdefinesDOCUMENTwithpriority: 2andVARIABLEwithpriority: 1, anddetectTokenPatternssorts by descending priority. This now matches the “higher priority number = higher priority” comment and ensures document patterns are attempted before variable patterns. This resolves the earlier ambiguity around pattern precedence.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dan-niles did you change this or is it from a merge commit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it came from rebasing. Previously I sent the PR to bi-1.5.x branch and switched it to main.
There was a problem hiding this 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 comprehensive support for inserting multi-modal documents (images, files, and audio) into AI prompts via the expression helper pane, enhancing the low-code user experience. The implementation includes a new template mode with markdown editing capabilities, token-based expression rendering, and improved UI for document selection.
Key Changes:
- Added Documents section to helper pane for
ai:Promptfields with support for Image, File, and Audio document types - Implemented template mode with markdown toolbar, preview functionality, and chip-based token rendering
- Enhanced expression editor with backtick hiding and improved cursor/selection management
- Added URL-based document creation and variable filtering for compatible types
Reviewed Changes
Copilot reviewed 47 out of 51 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/Documents.tsx |
New Documents view listing supported AI document types |
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx |
Document configuration page with variable selection and URL input |
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx |
New template mode component with markdown toolbar and preview |
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts |
Extended type system for template mode and document tokens |
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts |
Enhanced token handling with compound sequence detection |
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx |
Updated to support template mode with sanitization logic |
workspaces/ballerina/ballerina-side-panel/package.json |
Added rehype-raw and remark-gfm dependencies for markdown rendering |
Files not reviewed (1)
- common/config/rush/pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
workspaces/common-libs/ui-toolkit/src/components/Commons/ErrorBanner.tsx:1
- The inline type annotation and spreading of
sxprop is redundant. The component already hassxtyped in the props interface (line 23). Remove the inline type annotation and simplify to${props => props.sx && { ...props.sx }}.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Purpose
Closes wso2/product-ballerina-integrator#1840
This PR introduces support for inserting multi-modal documents (images, files, and audio) into prompts for model providers via the expression helper pane. This enhancement removes the need for users to manually type the document syntax and significantly improves the low-code user experience.
Adding new documents through helper pane:
Screen.Recording.2025-11-14.at.9.15.32.AM.mov
Replacing existing document chips:
Screen.Recording.2025-11-14.at.9.45.07.AM.mov
Template<->Expressionmode switcher:Screen.Recording.2025-11-19.at.12.30.00.PM.mov
Expanded editor with markdown toolbar and preview:
Screen.Recording.2025-11-19.at.12.32.22.PM.mov
Goals
This feature enables users to easily add
ai:ImageDocument,ai:FileDocument, andai:AudioDocumentobjects into the expression editor through the helper pane, addressing the current limitation where users must manually type complex syntax.It also enhances the helper pane to filter and display only valid variable types when inserting documents (AI Document types,
string,ai:Url,byte[]) and introduces a more intuitive UI for selecting or providing document inputs.Approach
ai:Prompt.Add from URLsupport for direct URL-based document inputs.(See screenshots above for the updated UI.)
User stories
This PR addresses the following user stories:
Release note
Introduces support for inserting Image, File, and Audio documents into prompt expressions via an improved helper pane, enhancing multi-modal prompt creation in the expression editor.
Summary by CodeRabbit
New Features
Bug Fixes
UI/UX
✏️ Tip: You can customize this high-level summary in your review settings.