Flexibles KI Prompt System #48
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
import { api } from '../utils/api'
|
import { api } from '../utils/api'
|
||||||
import { X, Plus, Trash2, MoveUp, MoveDown, Code } from 'lucide-react'
|
import { X, Plus, Trash2, MoveUp, MoveDown, Code } from 'lucide-react'
|
||||||
import PlaceholderPicker from './PlaceholderPicker'
|
import PlaceholderPicker from './PlaceholderPicker'
|
||||||
|
|
@ -39,6 +39,8 @@ export default function UnifiedPromptModal({ prompt, onSave, onClose }) {
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [showPlaceholderPicker, setShowPlaceholderPicker] = useState(false)
|
const [showPlaceholderPicker, setShowPlaceholderPicker] = useState(false)
|
||||||
const [pickerTarget, setPickerTarget] = useState(null) // 'base' or {stage, promptIdx}
|
const [pickerTarget, setPickerTarget] = useState(null) // 'base' or {stage, promptIdx}
|
||||||
|
const [cursorPosition, setCursorPosition] = useState(null) // Track cursor position for insertion
|
||||||
|
const baseTemplateRef = useRef(null)
|
||||||
|
|
||||||
// Test functionality
|
// Test functionality
|
||||||
const [testing, setTesting] = useState(false)
|
const [testing, setTesting] = useState(false)
|
||||||
|
|
@ -514,9 +516,12 @@ export default function UnifiedPromptModal({ prompt, onSave, onClose }) {
|
||||||
<div style={{ marginBottom: 24 }}>
|
<div style={{ marginBottom: 24 }}>
|
||||||
<h3 style={{ fontSize: 16, fontWeight: 600, marginBottom: 12 }}>Template</h3>
|
<h3 style={{ fontSize: 16, fontWeight: 600, marginBottom: 12 }}>Template</h3>
|
||||||
<textarea
|
<textarea
|
||||||
|
ref={baseTemplateRef}
|
||||||
className="form-input"
|
className="form-input"
|
||||||
value={template}
|
value={template}
|
||||||
onChange={e => setTemplate(e.target.value)}
|
onChange={e => setTemplate(e.target.value)}
|
||||||
|
onClick={e => setCursorPosition(e.target.selectionStart)}
|
||||||
|
onKeyUp={e => setCursorPosition(e.target.selectionStart)}
|
||||||
rows={12}
|
rows={12}
|
||||||
placeholder="Prompt-Template mit {{placeholders}}..."
|
placeholder="Prompt-Template mit {{placeholders}}..."
|
||||||
style={{ width: '100%', textAlign: 'left', resize: 'vertical', fontFamily: 'monospace', fontSize: 12 }}
|
style={{ width: '100%', textAlign: 'left', resize: 'vertical', fontFamily: 'monospace', fontSize: 12 }}
|
||||||
|
|
@ -799,10 +804,22 @@ export default function UnifiedPromptModal({ prompt, onSave, onClose }) {
|
||||||
<PlaceholderPicker
|
<PlaceholderPicker
|
||||||
onSelect={(placeholder) => {
|
onSelect={(placeholder) => {
|
||||||
if (pickerTarget === 'base') {
|
if (pickerTarget === 'base') {
|
||||||
// Insert into base template
|
// Insert into base template at cursor position
|
||||||
setTemplate(template + placeholder)
|
const pos = cursorPosition ?? template.length
|
||||||
|
const newTemplate = template.slice(0, pos) + placeholder + template.slice(pos)
|
||||||
|
setTemplate(newTemplate)
|
||||||
|
|
||||||
|
// Restore focus and cursor position after insertion
|
||||||
|
setTimeout(() => {
|
||||||
|
if (baseTemplateRef.current) {
|
||||||
|
baseTemplateRef.current.focus()
|
||||||
|
const newPos = pos + placeholder.length
|
||||||
|
baseTemplateRef.current.setSelectionRange(newPos, newPos)
|
||||||
|
setCursorPosition(newPos)
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
} else if (pickerTarget && typeof pickerTarget === 'object') {
|
} else if (pickerTarget && typeof pickerTarget === 'object') {
|
||||||
// Insert into pipeline stage template
|
// Insert into pipeline stage template (append at end for now)
|
||||||
const { stage: stageNum, promptIdx } = pickerTarget
|
const { stage: stageNum, promptIdx } = pickerTarget
|
||||||
setStages(stages.map(s => {
|
setStages(stages.map(s => {
|
||||||
if (s.stage === stageNum) {
|
if (s.stage === stageNum) {
|
||||||
|
|
|
||||||
|
|
@ -103,12 +103,13 @@ export default function Analysis() {
|
||||||
|
|
||||||
const deleteInsight = async (id) => {
|
const deleteInsight = async (id) => {
|
||||||
if (!confirm('Analyse löschen?')) return
|
if (!confirm('Analyse löschen?')) return
|
||||||
const pid = localStorage.getItem('bodytrack_active_profile')||''
|
try {
|
||||||
await fetch(`/api/insights/${id}`, {
|
await api.deleteInsight(id)
|
||||||
method:'DELETE', headers: pid ? {'X-Profile-Id':pid} : {}
|
if (newResult?.id === id) setNewResult(null)
|
||||||
})
|
await loadAll()
|
||||||
if (newResult?.id === id) setNewResult(null)
|
} catch (e) {
|
||||||
await loadAll()
|
setError('Löschen fehlgeschlagen: ' + e.message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group insights by scope for history view
|
// Group insights by scope for history view
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ export const api = {
|
||||||
insightPipeline: () => req('/insights/pipeline',{method:'POST'}),
|
insightPipeline: () => req('/insights/pipeline',{method:'POST'}),
|
||||||
listInsights: () => req('/insights'),
|
listInsights: () => req('/insights'),
|
||||||
latestInsights: () => req('/insights/latest'),
|
latestInsights: () => req('/insights/latest'),
|
||||||
|
deleteInsight: (id) => req(`/insights/${id}`, {method:'DELETE'}),
|
||||||
exportZip: async () => {
|
exportZip: async () => {
|
||||||
const res = await fetch(`${BASE}/export/zip`, {headers: hdrs()})
|
const res = await fetch(`${BASE}/export/zip`, {headers: hdrs()})
|
||||||
if (!res.ok) throw new Error('Export failed')
|
if (!res.ok) throw new Error('Export failed')
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user