feat(apollo-wind): add PromptEditor component [MST-10659]#784
feat(apollo-wind): add PromptEditor component [MST-10659]#784fikewa-olatunji wants to merge 9 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Ports a Lexical-based PromptEditor into @uipath/apollo-wind as a reusable component, including token-pill rendering, $-triggered variable autocomplete, formatting toolbar, and a sanitized markdown preview mode.
Changes:
- Added
PromptEditorpublic exports (+ supporting types) and integrated Lexical-based editor with token nodes + plugins. - Implemented toolbar actions, token copy/paste serialization,
$-autocomplete menu, validation, and rename-on-options-change behavior. - Added markdown preview rendering via
marked+dompurify, plus Storybook + unit tests; updated dependencies/lockfile.
Reviewed changes
Copilot reviewed 35 out of 36 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Locks new Lexical + markdown preview dependencies and transitive graph. |
| packages/apollo-wind/package.json | Adds Lexical + marked + dompurify runtime deps for the new component. |
| packages/apollo-wind/src/index.ts | Exports PromptEditor + types from the package root. |
| packages/apollo-wind/src/components/ui/prompt-editor/index.ts | Local barrel export for PromptEditor and its public types. |
| packages/apollo-wind/src/components/ui/prompt-editor/types.ts | Defines token/types API and token color/label helpers. |
| packages/apollo-wind/src/components/ui/prompt-editor/prompt-editor.tsx | Main component wiring Lexical composer, plugins, toolbar, preview, and imperative ref API. |
| packages/apollo-wind/src/components/ui/prompt-editor/prompt-editor.test.tsx | Unit tests for rendering, toolbar/preview toggling, and malformed-prop tolerance. |
| packages/apollo-wind/src/components/ui/prompt-editor/prompt-editor.stories.tsx | Storybook coverage for common modes (toolbar, autocomplete, preview, controlled, etc.). |
| packages/apollo-wind/src/components/ui/prompt-editor/utils/index.ts | Barrel export for prompt-editor utilities. |
| packages/apollo-wind/src/components/ui/prompt-editor/utils/serialization.ts | Token ↔ Lexical state conversion + clipboard string serialization/parsing + selection token extraction. |
| packages/apollo-wind/src/components/ui/prompt-editor/utils/insert-token.ts | Inserts token nodes at cursor / selection. |
| packages/apollo-wind/src/components/ui/prompt-editor/utils/comparison.ts | Token-array deep equality helper. |
| packages/apollo-wind/src/components/ui/prompt-editor/utils/autocomplete-segments.ts | $ trigger helpers (type inference, regex, dismissed-trigger sentinel). |
| packages/apollo-wind/src/components/ui/prompt-editor/plugins/ValueSyncPlugin.tsx | Controlled-value sync into Lexical state with focus-aware selection preservation. |
| packages/apollo-wind/src/components/ui/prompt-editor/plugins/ValidateTokensPlugin.tsx | Marks token pills invalid if not present in autocomplete option set. |
| packages/apollo-wind/src/components/ui/prompt-editor/plugins/RenameTokensPlugin.tsx | Renames token values when option-path trees indicate a rename. |
| packages/apollo-wind/src/components/ui/prompt-editor/plugins/AutocompletePlugin.tsx | Drives $ trigger detection and commits selected/free-form variables as token pills. |
| packages/apollo-wind/src/components/ui/prompt-editor/plugins/CopyPastePlugin.tsx | Custom copy/cut/paste integrating token clipboard format and Lexical mime payload. |
| packages/apollo-wind/src/components/ui/prompt-editor/plugins/EditorRefPlugin.tsx | Exposes Lexical editor instance to parent wiring. |
| packages/apollo-wind/src/components/ui/prompt-editor/plugins/MultilinePlugin.tsx | Enforces single-line vs multiline behavior (enter suppression, newline stripping). |
| packages/apollo-wind/src/components/ui/prompt-editor/plugins/NodeSelectionFixPlugin.tsx | Keyboard/cursor management for inline DecoratorNode “pill” selection. |
| packages/apollo-wind/src/components/ui/prompt-editor/plugins/ToolbarActionsPlugin.tsx | Implements formatting actions by inserting markdown markers/prefixes. |
| packages/apollo-wind/src/components/ui/prompt-editor/plugins/shared/token-nodes.ts | Shared traversal helpers for finding token nodes in editor state. |
| packages/apollo-wind/src/components/ui/prompt-editor/nodes/index.ts | Barrel export for the custom Lexical token nodes. |
| packages/apollo-wind/src/components/ui/prompt-editor/nodes/InputTokenNode.tsx | Input token DecoratorNode implementation. |
| packages/apollo-wind/src/components/ui/prompt-editor/nodes/OutputTokenNode.tsx | Output token DecoratorNode implementation. |
| packages/apollo-wind/src/components/ui/prompt-editor/nodes/StateTokenNode.tsx | State token DecoratorNode implementation. |
| packages/apollo-wind/src/components/ui/prompt-editor/nodes/ResourceTokenNode.tsx | Resource token DecoratorNode implementation. |
| packages/apollo-wind/src/components/ui/prompt-editor/components/EditorToolbar.tsx | Toolbar UI (edit/preview toggle + formatting buttons). |
| packages/apollo-wind/src/components/ui/prompt-editor/components/PromptEditorAutocompleteMenu.tsx | Caret-anchored command menu for variable selection. |
| packages/apollo-wind/src/components/ui/prompt-editor/components/MarkdownPreview.tsx | Markdown preview rendering + sanitization + token-pill HTML injection. |
| packages/apollo-wind/src/components/ui/prompt-editor/components/markdown-preview.css | Styles for preview output and token pills in sanitized HTML. |
| packages/apollo-wind/src/components/ui/prompt-editor/components/TokenPill.tsx | Visual pill rendering (icons, remove button, selected outline). |
| packages/apollo-wind/src/components/ui/prompt-editor/components/TokenPillWithTooltip.tsx | Adds tooltips + NodeSelection mouse handling to token pills. |
| packages/apollo-wind/src/components/ui/prompt-editor/components/token-icon-markup.ts | Inline lucide SVG markup builder for preview-mode token icons. |
| packages/apollo-wind/src/components/ui/prompt-editor/components/token-icon-markup.test.ts | Tests for SVG markup builder output. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
b5f8a7a to
9168e44
Compare
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Dependency License Review
License distribution
Excluded packages
|
9168e44 to
7fc6691
Compare
7fc6691 to
10b1152
Compare
10b1152 to
645a2fb
Compare
645a2fb to
676ed08
Compare
676ed08 to
d64f988
Compare
d64f988 to
b30a8cb
Compare
e1dd32d to
b47504a
Compare
559bc21 to
11bb2a4
Compare
CalinaCristian
left a comment
There was a problem hiding this comment.
PR #784 review — PromptEditor extraction
Overall: correct, intentional decoupling — not a strict 1-to-1 but a well-executed port. Core Lexical engine, plugin architecture, serialization, and public API surface are faithfully preserved. Several real bugs from the flow-workbench original were fixed along the way (empty-array controlled value, minRows/maxRows clamping, Lexical state corruption in insertVariableToken, $-prefix on free-typed chip paths, NodeSelection crash guard, borderless text color). No data-loss or crash bugs found in the new code.
Severity legend: 🟡 medium = worth landing before merge · 🔵 low = follow-up is fine
✅ Bug fixes shipped in this extraction (all genuine improvements)
| Fix | Location |
|---|---|
root.append(tokenNode) → wrap in $createParagraphNode() |
insert-token.ts |
!value → value === undefined — empty array now clears a controlled editor |
ValueSyncPlugin.tsx |
isEmpty init seeds from initialValue ?? value — fixes controlled-only usage |
prompt-editor.tsx |
effectiveMinRows = Math.min(minRows, maxRows) — max-height no longer silently overridden |
prompt-editor.tsx |
borderless ? 'inherit' : 'var(--color-foreground)' — correct text color in parent-provided chrome |
prompt-editor.tsx |
$isRangeSelection guard — insertRawText can't throw on a NodeSelection |
prompt-editor.tsx |
Free-typed chip path: $vars.foo → vars.foo — matches menu-selected chips for validation |
AutocompletePlugin.tsx |
closeMenu(explicit) flag — auto-close no longer suppresses re-opening at the same $ position |
AutocompletePlugin.tsx |
target removed from DOMPurify allowlist — closes tabnabbing vector in markdown preview |
MarkdownPreview.tsx |
CSS inlined in JS — self-contained bundle, no unresolved .css import next to published JS |
MarkdownPreview.tsx |
Inline comments below cover the two 🟡 medium and one 🔵 low observations.
| export interface TokenPillProps { | ||
| value: string; | ||
| /** Token type — selects the leading icon and (via the wrapper) the tooltip label. */ | ||
| tokenType: PromptEditorTokenType; |
There was a problem hiding this comment.
🔵 L1 — size='md' variant not ported
The flow-workbench TokenPill had size?: 'sm' | 'md' for a taller standalone single-variable field (28px chip, larger text). The port hardcodes sm geometry and only accepts tokenType.
No current apollo-wind consumer needs the medium size, so this is fine to ship as-is. Flagging for when a standalone single-token field is added to the design system — the variant will need to be re-introduced, ideally as a named size prop to keep the API consistent with other apollo-wind components.
There was a problem hiding this comment.
Agreed — leaving sm-only for now since no apollo-wind consumer needs the medium size. Will reintroduce it as a named size prop when a standalone single-token field is added, to keep the API consistent.
| type Story = StoryObj<typeof meta>; | ||
|
|
||
| const AUTOCOMPLETE_OPTIONS: PromptEditorAutoCompleteOption[] = [ | ||
| { type: 'input', value: 'vars.firstName' }, |
There was a problem hiding this comment.
Can we document the token value convention for PromptEditorAutoCompleteOption.value and token values?
The stories/tests use values like vars.firstName, while flow-workbench currently uses $vars.firstName. That is fine if Apollo’s public API intentionally owns the no-$ convention, but it should be explicit because serialization/validation depend on exact string equality.
There was a problem hiding this comment.
Documented it on PromptEditorToken and PromptEditorAutoCompleteOption in types.ts: apollo-wind owns the no-$ convention (e.g. vars.firstName), and serialization + ValidateTokensPlugin compare by exact string equality, so option values and token values must use the same $-less form. cecde50.
There was a problem hiding this comment.
can you experiment with publishing a dev package for this (dev-packages label and try consume it in flow-workbench to replace existing one and verify there are no regressions?)
There was a problem hiding this comment.
Validated via the dev package + flow-workbench swap — drop-in clean except mapVarDropToToken (drag-drop, intentionally not ported). Details in the preview-parity thread.
Port the Lexical-based PromptEditor from flow-workbench into apollo-wind as a reusable, prop-driven component (value / onChange / autoCompleteOptions). It renders inline token pills (input/output/state/resource), a formatting toolbar, a markdown preview mode, and a $-triggered variable autocomplete rebuilt on apollo-wind Command/Popover/Tooltip — decoupled from flow-workbench's variables, schema, i18n, and theme systems. Adds lexical, @lexical/react, @lexical/utils, @lexical/clipboard, marked and dompurify. Token/option props are normalized to arrays so malformed input can't crash the editor, and token colors map to apollo-wind design tokens. Exports PromptEditor and its public types from the package root, with unit tests and Storybook stories. Refs: MST-10659
- Honor `borderless` in preview mode: drop the editor's own border/background there too, so borderless behaves consistently between edit and preview. - Remove dead `data-invalid` style rule + allowed attribute in MarkdownPreview (preview no longer emits it). - Fix stale lucide version reference (0.555.0 -> 0.577.0) in token-icon-markup.
apollo-react pins lexical 0.16.0 (its ApRichTextEditor); apollo-wind's PromptEditor requires lexical 0.42.0 (React 19 support + 0.42-only APIs). The two design-system packages intentionally track different lexical majors, so ignore the lexical/@lexical/* family in the consistency check rather than force-aligning and breaking one of the editors.
…focus, preview styles, exports) - PromptEditorAutocompleteMenu: preventDefault on container pointerdown so focus can't leave the editor before commit (review M1) - Move markdown-preview styles from an inline <style> into the shared package stylesheet (tailwind.utilities.css) consumers already import - Remove unused PromptEditorHighlightLocator/Item types (review M2) - Export prompt-editor from the components/ui barrel - Document the no-$ token value convention and that preview is visual-only re: validity
…lity + scope dep exemption - Narrow the dependency-consistency exemption from the repo-wide lexical family to just @uipath/apollo-wind, so lexical drift elsewhere is still caught (review) - Add unit tests for clipboard token serialization round-trip, free-form path type inference + VARIABLE_PATH_REGEX, dismissed-trigger suppression, and token equality (the extraction-risky logic; Lexical interaction paths stay limited under jsdom)
e1e2aea to
368ac01
Compare
Deployed apollo-design Storybook already themes the autodocs story background, so the borderless PromptEditor preview is readable without this override. Reverts preview-head.html to match main (review).
…mption Bumps apollo-react's lexical + @lexical/* from 0.16.0 to 0.42.0 to match apollo-wind, so the dependency-version-consistency check passes without any ignore workaround (review). Fixes the one resulting breakage: LexicalErrorBoundary is a named export in 0.42, not default. apollo-react editor typechecks clean and its 18 tests pass on 0.42.
📦 Dev Packages
|
Adds a native HTML5 drag-drop VariableDropPlugin + `mapVarDropToToken` prop: a consumer's drag source sets the variable path on dataTransfer (VARIABLE_DRAG_MIME, now exported), and the editor inserts a token at the drop point (falling back to the end of the content when the drop caret can't be resolved). Only the custom MIME is handled, so ordinary text drops aren't hijacked. Wired through PromptEditor → EditorInner, with a WithVariableDragDrop story and tests.
- Free-form $path commit now happens in the picker (an 'Insert <path>' item) so Enter commits it, instead of an editor handler the focused menu never received. - VARIABLE_PATH_REGEX accepts state/resource namespaces; inferTokenTypeFromPath falls back to the leading namespace (state.*→state, resource.*→resource) so free-form chips get the correct node type. - wrapSelectionWithMarkers normalizes selection.isBackward() so right-to-left selections wrap correctly. - RenameTokensPlugin groups renames by type once (O(1) per-node lookup).
Jira: MST-10659
Summary
Ports the Lexical-based PromptEditor from flow-workbench into
@uipath/apollo-windas a reusable, fully prop-driven component. It renders inline variable token pills (input / output / state / resource), a formatting toolbar, a markdown preview mode, and a$-triggered variable autocomplete.The port deliberately decouples the editor from flow-workbench-specific systems that don't exist in a design system:
Command/Popover/Tooltip), driven by theautoCompleteOptionsprop — no flowVariablePickeror schema services.react-i18next).Public API
PromptEditor+ types (PromptEditorProps,PromptEditorRef,PromptEditorToken,PromptEditorTokenType,PromptEditorAutoCompleteOption,PromptEditorMode) exported from the package root. Controlled (value/onChange) and uncontrolled (initialValue) usage,multiline,minRows/maxRows,placeholder,disabled,showToolbar,mode/onModeChange,borderless,fillHeight, and an imperativeeditorRef.Dependencies added (apollo-wind only)
lexical,@lexical/react,@lexical/utils,@lexical/clipboard(0.42.0),marked,dompurify. No other package uses them (version-consistency safe); they introduce no new audit findings.Robustness
Token/option props are normalized to arrays so malformed input (e.g. a Storybook "Set object"
{}) can't crash the editor or preview.maxRowscaps the visible height and scrolls (clamped so it isn't overridden whenmaxRows ≤ minRows).Testing
vitest: 19 unit tests for PromptEditor (rendering, toolbar, preview, mode toggle, autocomplete mount, malformed-input tolerance) — full apollo-wind suite passes (928 tests).biome format/lintclean,test:coveragepasses, fullpnpm build(all 8 packages) succeeds, frozen lockfile in sync.$-autocomplete, chip insertion, light/dark, height capping) verified end-to-end in a headless browser.