Skip to content

Instantly share code, notes, and snippets.

@azizpunjani
Last active February 6, 2026 22:17
Show Gist options
  • Select an option

  • Save azizpunjani/28f6ae5ee6f2c4512e03bb893bfa5811 to your computer and use it in GitHub Desktop.

Select an option

Save azizpunjani/28f6ae5ee6f2c4512e03bb893bfa5811 to your computer and use it in GitHub Desktop.
AI Text Transform - Implementation Analysis (streaming, patterns, recommendations) - Updated

AI Text Transform - Architecture Decision

Overview

Implement AI text refinement features (shorten, elaborate, professional) for SmartPages/Lexical editor.


The Problem

We need to create:

  1. A hook (useAITextRefine) - manages API calls, loading state, results
  2. A panel (AIRefinePanel) - UI for selecting presets and displaying results

Constraint: The API layer (endpoints, auth, CSRF, request body format) lives in client/ code. Shared packages cannot import from client/.

Question: How should the Panel consume the hook?


Options

Option 1: Everything in Client

Put both the hook and the panel in client/ code.

// client/features/shared/components/AIRefine/useAITextRefine.ts
// client/features/shared/components/AIRefine/AIRefinePanel.tsx

// Usage in Lexical plugin
const { transform, result, isLoading } = useAITextRefine();

<AIRefinePanel
  result={result}
  isLoading={isLoading}
  onTransform={(text, preset) => transform(text, preset)}
  onAccept={(text) => editor.replaceSelection(text)}
/>
Pros Cons
Simplest implementation Panel not reusable outside client
No cross-package dependencies Cannot be used by other packages
All code in one place Doesn't leverage common-ux package
Fastest to implement

Best for: Projects where reusability isn't a concern.


Option 2: Props Threading (Presentational Panel)

Hook lives in client/, Panel lives in @highspot/common-ux as a purely presentational component.

// client/features/shared/hooks/useAITextRefine.ts
const { transform, result, isLoading } = useAITextRefine();

// @highspot/common-ux - Panel is purely presentational
<AIRefinePanel
  text={selectedText}
  result={result}
  isLoading={isLoading}
  presets={['shorten', 'elaborate', 'professional']}
  onTransform={(text, preset) => transform(text, preset)}
  onAccept={(text) => editor.replaceSelection(text)}
  onCancel={() => closePanel()}
/>
Pros Cons
Panel is reusable across packages Verbose prop threading
Clear separation of concerns Consumer must wire up hook + panel each time
No business logic in shared package More boilerplate at usage sites
Panel is easy to test (pure props)

Best for: When you want a reusable UI component but consumers are comfortable with manual wiring.


Option 3: Composition Wrapper (Recommended)

Hook lives in client/, Panel lives in @highspot/common-ux, plus a "smart" wrapper in client/ that combines them.

// @highspot/common-ux - Presentational panel (reusable)
<AIRefinePanel
  text={...}
  result={...}
  isLoading={...}
  onTransform={...}
  onAccept={...}
  onCancel={...}
/>

// client/features/shared/hooks/useAITextRefine.ts - Hook (API logic)
const { transform, result, isLoading } = useAITextRefine();

// client/features/shared/components/AIRefine.tsx - Smart wrapper
export const AIRefine = ({ text, onAccept, onCancel }) => {
  const { transform, result, isLoading } = useAITextRefine();

  return (
    <AIRefinePanel
      text={text}
      result={result}
      isLoading={isLoading}
      onTransform={transform}
      onAccept={onAccept}
      onCancel={onCancel}
    />
  );
};

// Usage in Lexical plugin - clean!
<AIRefine
  text={selectedText}
  onAccept={(text) => editor.replaceSelection(text)}
  onCancel={() => closePanel()}
/>
Pros Cons
Clean DX for consumers Two components to maintain
Panel remains reusable in common-ux Slight indirection
Smart wrapper hides wiring complexity
Follows composition pattern
Easy to add features (streaming, etc.) to wrapper

Best for: When you want both reusability AND clean consumer DX.


Recommendation

Option 3: Composition Wrapper

This gives us the best of both worlds:

  • The AIRefinePanel in @highspot/common-ux is purely presentational and can be reused anywhere
  • The AIRefine smart wrapper in client/ provides a clean API for the common use case
  • Consumers who need custom behavior can use the Panel directly with their own hook

Implementation Plan

Component Location Purpose
useAITextRefine client/features/shared/hooks/ Hook that calls AI API
AIRefinePanel @highspot/common-ux Presentational UI component
AIRefine client/features/shared/components/ Smart wrapper combining hook + panel

JIRA Tickets

JIRA Key Description Status
HS-157068 Register AI Refine prompts in GrowthBook Not started
HS-157069 Create new package for AI features ✅ Done
HS-157070 Create useAITextTransform hook 🔄 In Progress
HS-157071 Add streaming support Not started
HS-157072 Create AIRefinePanel component Not started
HS-157073 Add Storybook stories Not started
HS-157074 Create Lexical AIRefinePlugin Not started
HS-157075 Feature flag setup Not started
HS-157076 E2E tests Not started

Key Reference Files

Purpose Path
Existing hook pattern client/features/shared/components/ai/RoutesAIServices/hooks/useGeneralAIRequest.js
API request helper client/features/shared/components/ai/RoutesAIServices/helpers/sendAIControlGeneralRequest.js
Streaming client client/features/copilot/components/CopilotCenter/agent-platform/chatui/ChatUIStreamClient.ts
Routes/endpoints client/features/shared/components/ai/RoutesAIServices/RoutesAIServices.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment