feat(kol): add selection-based AI optimize for prompt editor
Trigger an AI optimize panel from a text selection in the KOL prompt editor, accept a user instruction, and stream a preview before replacing the selected fragment. Extend the shared editor AI assist panel with boundary-aware placement so it can be hosted inside scoped surfaces, and update the backend optimize prompt to honor the user instruction. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -145,6 +145,7 @@ type ImageContextMenuAction =
|
||||
type ImagePickerAction = "insert" | "replace";
|
||||
|
||||
type AiOptimizeStatus = "idle" | "generating" | "completed" | "error";
|
||||
type EditorAiAssistPlacement = "bottom" | "top";
|
||||
|
||||
type TableContextMenuState = {
|
||||
open: boolean;
|
||||
@@ -175,6 +176,7 @@ type AiOptimizePanelState = {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
placement: EditorAiAssistPlacement;
|
||||
from: number;
|
||||
to: number;
|
||||
selectedText: string;
|
||||
@@ -255,6 +257,7 @@ function createDefaultAiOptimizePanelState(): AiOptimizePanelState {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
placement: "bottom",
|
||||
from: 0,
|
||||
to: 0,
|
||||
selectedText: "",
|
||||
@@ -941,20 +944,29 @@ function resolveTextSelectionSnapshot(ctx: Ctx): TextSelectionSnapshot | null {
|
||||
};
|
||||
}
|
||||
|
||||
function resolveAiOptimizePanelFrame(anchorRect: DOMRect): { x: number; y: number; width: number } {
|
||||
function resolveAiOptimizePanelFrame(anchorRect: DOMRect): { x: number; y: number; width: number; placement: EditorAiAssistPlacement } {
|
||||
const viewportPadding = 16;
|
||||
const panelPadding = 24;
|
||||
const maxWidth = Math.max(240, window.innerWidth - viewportPadding * 2);
|
||||
const preferredWidth = 1120;
|
||||
|
||||
const surfaceRect = surfaceRef.value?.getBoundingClientRect();
|
||||
const boundary = surfaceRect ?? new DOMRect(0, 0, window.innerWidth, window.innerHeight);
|
||||
const widthFromSurface = surfaceRect ? Math.max(240, surfaceRect.width - panelPadding * 2) : preferredWidth;
|
||||
const width = Math.min(maxWidth, preferredWidth, widthFromSurface);
|
||||
const availableBelow = boundary.bottom - anchorRect.bottom - AI_OPTIMIZE_PANEL_GAP - viewportPadding;
|
||||
const availableAbove = anchorRect.top - boundary.top - AI_OPTIMIZE_PANEL_GAP - viewportPadding;
|
||||
const placement: EditorAiAssistPlacement =
|
||||
availableBelow >= 320 || availableBelow >= availableAbove ? "bottom" : "top";
|
||||
|
||||
return {
|
||||
x: anchorRect.left + anchorRect.width / 2,
|
||||
y: Math.max(viewportPadding, anchorRect.bottom + AI_OPTIMIZE_PANEL_GAP),
|
||||
y:
|
||||
placement === "top"
|
||||
? Math.min(boundary.bottom - viewportPadding, anchorRect.top - AI_OPTIMIZE_PANEL_GAP)
|
||||
: Math.max(boundary.top + viewportPadding, anchorRect.bottom + AI_OPTIMIZE_PANEL_GAP),
|
||||
width,
|
||||
placement,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -981,6 +993,7 @@ function openAiOptimizePanel(ctx: Ctx): void {
|
||||
x: frame.x,
|
||||
y: frame.y,
|
||||
width: frame.width,
|
||||
placement: frame.placement,
|
||||
from: snapshot.from,
|
||||
to: snapshot.to,
|
||||
selectedText: snapshot.text,
|
||||
@@ -1975,6 +1988,8 @@ function runTableContextAction(action: TableContextMenuAction): void {
|
||||
:x="aiOptimizePanel.x"
|
||||
:y="aiOptimizePanel.y"
|
||||
:width="aiOptimizePanel.width"
|
||||
:placement="aiOptimizePanel.placement"
|
||||
:boundary-element="surfaceRef"
|
||||
:status="aiOptimizeStatus"
|
||||
:streaming="aiOptimizeStreaming"
|
||||
:has-preview="aiOptimizeHasPreview"
|
||||
|
||||
Reference in New Issue
Block a user