mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-20 20:38:16 -05:00
Compare commits
33 Commits
feat/email
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dff1c9d083 | ||
|
|
e4ad31bb6b | ||
|
|
84691fc873 | ||
|
|
b09f683072 | ||
|
|
a8bb0db660 | ||
|
|
af82820a28 | ||
|
|
4372841797 | ||
|
|
5e8c843241 | ||
|
|
7bf3d73ee6 | ||
|
|
7ffc11a738 | ||
|
|
be578e2ed7 | ||
|
|
f415e5edc4 | ||
|
|
13a6e6c3fa | ||
|
|
f5ab7f21ae | ||
|
|
bfb6fffe38 | ||
|
|
4fbec0a43f | ||
|
|
585f5e365b | ||
|
|
3792bdd252 | ||
|
|
eb5d1f3e5b | ||
|
|
54ab82c8dd | ||
|
|
f895bf469b | ||
|
|
dd3209af06 | ||
|
|
b6ba3b50a7 | ||
|
|
b304233062 | ||
|
|
57e4b49bd6 | ||
|
|
e12dd204ed | ||
|
|
3d9d9cbc54 | ||
|
|
0f4ec962ad | ||
|
|
4827866f9a | ||
|
|
3e697d9ed9 | ||
|
|
4431a1a484 | ||
|
|
4d1a9a3f22 | ||
|
|
eb07a080fb |
@@ -538,15 +538,11 @@ export function Document({
|
||||
},
|
||||
{
|
||||
onSuccess: (result) => {
|
||||
if (operation === 'delete') {
|
||||
if (operation === 'delete' || result.errorCount > 0) {
|
||||
refreshChunks()
|
||||
} else {
|
||||
result.results.forEach((opResult) => {
|
||||
if (opResult.operation === operation) {
|
||||
opResult.chunkIds.forEach((chunkId: string) => {
|
||||
updateChunk(chunkId, { enabled: operation === 'enable' })
|
||||
})
|
||||
}
|
||||
chunks.forEach((chunk) => {
|
||||
updateChunk(chunk.id, { enabled: operation === 'enable' })
|
||||
})
|
||||
}
|
||||
logger.info(`Successfully ${operation}d ${result.successCount} chunks`)
|
||||
|
||||
@@ -462,7 +462,7 @@ export function BaseTagsModal({ open, onOpenChange, knowledgeBaseId }: BaseTagsM
|
||||
<ModalHeader>Documents using "{selectedTag?.displayName}"</ModalHeader>
|
||||
<ModalBody>
|
||||
<div className='space-y-[8px]'>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
{selectedTagUsage?.documentCount || 0} document
|
||||
{selectedTagUsage?.documentCount !== 1 ? 's are' : ' is'} currently using this tag
|
||||
definition.
|
||||
@@ -470,7 +470,7 @@ export function BaseTagsModal({ open, onOpenChange, knowledgeBaseId }: BaseTagsM
|
||||
|
||||
{selectedTagUsage?.documentCount === 0 ? (
|
||||
<div className='rounded-[6px] border p-[16px] text-center'>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
This tag definition is not being used by any documents. You can safely delete it
|
||||
to free up the tag slot.
|
||||
</p>
|
||||
|
||||
@@ -283,7 +283,7 @@ export function GeneralDeploy({
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Promote to live</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
Are you sure you want to promote{' '}
|
||||
<span className='font-medium text-[var(--text-primary)]'>
|
||||
{versionToPromoteInfo?.name || `v${versionToPromote}`}
|
||||
|
||||
@@ -591,12 +591,11 @@ export function DeployModal({
|
||||
)}
|
||||
{activeTab === 'api' && (
|
||||
<ModalFooter className='items-center justify-between'>
|
||||
<div>
|
||||
<div />
|
||||
<div className='flex items-center gap-2'>
|
||||
<Button variant='default' onClick={() => setIsApiInfoModalOpen(true)}>
|
||||
Edit API Info
|
||||
</Button>
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Button
|
||||
variant='tertiary'
|
||||
onClick={() => setIsCreateKeyModalOpen(true)}
|
||||
|
||||
@@ -42,7 +42,7 @@ export function CodeEditor({
|
||||
placeholder = '',
|
||||
className = '',
|
||||
gutterClassName = '',
|
||||
minHeight = '360px',
|
||||
minHeight,
|
||||
highlightVariables = true,
|
||||
onKeyDown,
|
||||
disabled = false,
|
||||
@@ -186,7 +186,7 @@ export function CodeEditor({
|
||||
}
|
||||
|
||||
return (
|
||||
<Code.Container className={className} style={{ minHeight }}>
|
||||
<Code.Container className={className} style={minHeight ? { minHeight } : undefined}>
|
||||
{showWandButton && onWandClick && (
|
||||
<Button
|
||||
variant='ghost'
|
||||
@@ -220,7 +220,7 @@ export function CodeEditor({
|
||||
disabled={disabled}
|
||||
{...getCodeEditorProps({ disabled })}
|
||||
className={cn(getCodeEditorProps({ disabled }).className, 'h-full')}
|
||||
style={{ minHeight }}
|
||||
style={minHeight ? { minHeight } : undefined}
|
||||
textareaClassName={cn(
|
||||
getCodeEditorProps({ disabled }).textareaClassName,
|
||||
'!block !h-full !min-h-full'
|
||||
|
||||
@@ -87,15 +87,16 @@ export function CustomToolModal({
|
||||
const [codeError, setCodeError] = useState<string | null>(null)
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [toolId, setToolId] = useState<string | undefined>(undefined)
|
||||
const [initialJsonSchema, setInitialJsonSchema] = useState('')
|
||||
const [initialFunctionCode, setInitialFunctionCode] = useState('')
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
|
||||
const [showDiscardAlert, setShowDiscardAlert] = useState(false)
|
||||
const [isSchemaPromptActive, setIsSchemaPromptActive] = useState(false)
|
||||
const [schemaPromptInput, setSchemaPromptInput] = useState('')
|
||||
const [schemaPromptSummary, setSchemaPromptSummary] = useState<string | null>(null)
|
||||
const schemaPromptInputRef = useRef<HTMLInputElement | null>(null)
|
||||
|
||||
const [isCodePromptActive, setIsCodePromptActive] = useState(false)
|
||||
const [codePromptInput, setCodePromptInput] = useState('')
|
||||
const [codePromptSummary, setCodePromptSummary] = useState<string | null>(null)
|
||||
const codePromptInputRef = useRef<HTMLInputElement | null>(null)
|
||||
|
||||
const schemaGeneration = useWand({
|
||||
@@ -174,6 +175,9 @@ Example 2:
|
||||
generationType: 'custom-tool-schema',
|
||||
},
|
||||
currentValue: jsonSchema,
|
||||
onStreamStart: () => {
|
||||
setJsonSchema('')
|
||||
},
|
||||
onGeneratedContent: (content) => {
|
||||
setJsonSchema(content)
|
||||
setSchemaError(null)
|
||||
@@ -237,6 +241,9 @@ try {
|
||||
generationType: 'javascript-function-body',
|
||||
},
|
||||
currentValue: functionCode,
|
||||
onStreamStart: () => {
|
||||
setFunctionCode('')
|
||||
},
|
||||
onGeneratedContent: (content) => {
|
||||
handleFunctionCodeChange(content)
|
||||
setCodeError(null)
|
||||
@@ -272,12 +279,15 @@ try {
|
||||
|
||||
if (initialValues) {
|
||||
try {
|
||||
setJsonSchema(
|
||||
const schemaValue =
|
||||
typeof initialValues.schema === 'string'
|
||||
? initialValues.schema
|
||||
: JSON.stringify(initialValues.schema, null, 2)
|
||||
)
|
||||
setFunctionCode(initialValues.code || '')
|
||||
const codeValue = initialValues.code || ''
|
||||
setJsonSchema(schemaValue)
|
||||
setFunctionCode(codeValue)
|
||||
setInitialJsonSchema(schemaValue)
|
||||
setInitialFunctionCode(codeValue)
|
||||
setIsEditing(true)
|
||||
setToolId(initialValues.id)
|
||||
} catch (error) {
|
||||
@@ -304,17 +314,18 @@ try {
|
||||
const resetForm = () => {
|
||||
setJsonSchema('')
|
||||
setFunctionCode('')
|
||||
setInitialJsonSchema('')
|
||||
setInitialFunctionCode('')
|
||||
setSchemaError(null)
|
||||
setCodeError(null)
|
||||
setActiveSection('schema')
|
||||
setIsEditing(false)
|
||||
setToolId(undefined)
|
||||
setSchemaPromptSummary(null)
|
||||
setCodePromptSummary(null)
|
||||
setIsSchemaPromptActive(false)
|
||||
setIsCodePromptActive(false)
|
||||
setSchemaPromptInput('')
|
||||
setCodePromptInput('')
|
||||
setShowDiscardAlert(false)
|
||||
schemaGeneration.closePrompt()
|
||||
schemaGeneration.hidePromptInline()
|
||||
codeGeneration.closePrompt()
|
||||
@@ -328,31 +339,37 @@ try {
|
||||
onOpenChange(false)
|
||||
}
|
||||
|
||||
const validateJsonSchema = (schema: string): boolean => {
|
||||
if (!schema) return false
|
||||
const validateSchema = (schema: string): { isValid: boolean; error: string | null } => {
|
||||
if (!schema) return { isValid: false, error: null }
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(schema)
|
||||
|
||||
if (!parsed.type || parsed.type !== 'function') {
|
||||
return false
|
||||
return { isValid: false, error: 'Missing "type": "function"' }
|
||||
}
|
||||
|
||||
if (!parsed.function || !parsed.function.name) {
|
||||
return false
|
||||
return { isValid: false, error: 'Missing function.name field' }
|
||||
}
|
||||
|
||||
if (!parsed.function.parameters) {
|
||||
return false
|
||||
return { isValid: false, error: 'Missing function.parameters object' }
|
||||
}
|
||||
if (!parsed.function.parameters.type) {
|
||||
return { isValid: false, error: 'Missing parameters.type field' }
|
||||
}
|
||||
if (parsed.function.parameters.properties === undefined) {
|
||||
return { isValid: false, error: 'Missing parameters.properties field' }
|
||||
}
|
||||
if (
|
||||
typeof parsed.function.parameters.properties !== 'object' ||
|
||||
parsed.function.parameters.properties === null
|
||||
) {
|
||||
return { isValid: false, error: 'parameters.properties must be an object' }
|
||||
}
|
||||
|
||||
if (!parsed.function.parameters.type || parsed.function.parameters.properties === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (_error) {
|
||||
return false
|
||||
return { isValid: true, error: null }
|
||||
} catch {
|
||||
return { isValid: false, error: 'Invalid JSON format' }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,7 +391,32 @@ try {
|
||||
}
|
||||
}, [jsonSchema])
|
||||
|
||||
const isSchemaValid = useMemo(() => validateJsonSchema(jsonSchema), [jsonSchema])
|
||||
const isSchemaValid = useMemo(() => validateSchema(jsonSchema).isValid, [jsonSchema])
|
||||
|
||||
const hasChanges = useMemo(() => {
|
||||
if (!isEditing) return true
|
||||
return jsonSchema !== initialJsonSchema || functionCode !== initialFunctionCode
|
||||
}, [isEditing, jsonSchema, initialJsonSchema, functionCode, initialFunctionCode])
|
||||
|
||||
const hasUnsavedChanges = useMemo(() => {
|
||||
if (isEditing) {
|
||||
return jsonSchema !== initialJsonSchema || functionCode !== initialFunctionCode
|
||||
}
|
||||
return jsonSchema.trim().length > 0 || functionCode.trim().length > 0
|
||||
}, [isEditing, jsonSchema, initialJsonSchema, functionCode, initialFunctionCode])
|
||||
|
||||
const handleCloseAttempt = () => {
|
||||
if (hasUnsavedChanges && !schemaGeneration.isStreaming && !codeGeneration.isStreaming) {
|
||||
setShowDiscardAlert(true)
|
||||
} else {
|
||||
handleClose()
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfirmDiscard = () => {
|
||||
setShowDiscardAlert(false)
|
||||
handleClose()
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
@@ -384,43 +426,9 @@ try {
|
||||
return
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(jsonSchema)
|
||||
|
||||
if (!parsed.type || parsed.type !== 'function') {
|
||||
setSchemaError('Schema must have a "type" field set to "function"')
|
||||
setActiveSection('schema')
|
||||
return
|
||||
}
|
||||
|
||||
if (!parsed.function || !parsed.function.name) {
|
||||
setSchemaError('Schema must have a "function" object with a "name" field')
|
||||
setActiveSection('schema')
|
||||
return
|
||||
}
|
||||
|
||||
if (!parsed.function.parameters) {
|
||||
setSchemaError('Missing function.parameters object')
|
||||
setActiveSection('schema')
|
||||
return
|
||||
}
|
||||
|
||||
if (!parsed.function.parameters.type) {
|
||||
setSchemaError('Missing parameters.type field')
|
||||
setActiveSection('schema')
|
||||
return
|
||||
}
|
||||
|
||||
if (parsed.function.parameters.properties === undefined) {
|
||||
setSchemaError('Missing parameters.properties field')
|
||||
setActiveSection('schema')
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
typeof parsed.function.parameters.properties !== 'object' ||
|
||||
parsed.function.parameters.properties === null
|
||||
) {
|
||||
setSchemaError('parameters.properties must be an object')
|
||||
const { isValid, error } = validateSchema(jsonSchema)
|
||||
if (!isValid) {
|
||||
setSchemaError(error)
|
||||
setActiveSection('schema')
|
||||
return
|
||||
}
|
||||
@@ -483,17 +491,9 @@ try {
|
||||
}
|
||||
|
||||
onSave(customTool)
|
||||
|
||||
setSchemaPromptSummary(null)
|
||||
setCodePromptSummary(null)
|
||||
|
||||
handleClose()
|
||||
} catch (error) {
|
||||
logger.error('Error saving custom tool:', { error })
|
||||
|
||||
setSchemaPromptSummary(null)
|
||||
setCodePromptSummary(null)
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Failed to save custom tool'
|
||||
|
||||
if (errorMessage.includes('Cannot change function name')) {
|
||||
@@ -512,46 +512,8 @@ try {
|
||||
setJsonSchema(value)
|
||||
|
||||
if (value.trim()) {
|
||||
try {
|
||||
const parsed = JSON.parse(value)
|
||||
|
||||
if (!parsed.type || parsed.type !== 'function') {
|
||||
setSchemaError('Missing "type": "function"')
|
||||
return
|
||||
}
|
||||
|
||||
if (!parsed.function || !parsed.function.name) {
|
||||
setSchemaError('Missing function.name field')
|
||||
return
|
||||
}
|
||||
|
||||
if (!parsed.function.parameters) {
|
||||
setSchemaError('Missing function.parameters object')
|
||||
return
|
||||
}
|
||||
|
||||
if (!parsed.function.parameters.type) {
|
||||
setSchemaError('Missing parameters.type field')
|
||||
return
|
||||
}
|
||||
|
||||
if (parsed.function.parameters.properties === undefined) {
|
||||
setSchemaError('Missing parameters.properties field')
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
typeof parsed.function.parameters.properties !== 'object' ||
|
||||
parsed.function.parameters.properties === null
|
||||
) {
|
||||
setSchemaError('parameters.properties must be an object')
|
||||
return
|
||||
}
|
||||
|
||||
setSchemaError(null)
|
||||
} catch {
|
||||
setSchemaError('Invalid JSON format')
|
||||
}
|
||||
const { error } = validateSchema(value)
|
||||
setSchemaError(error)
|
||||
} else {
|
||||
setSchemaError(null)
|
||||
}
|
||||
@@ -709,12 +671,12 @@ try {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setSchemaParamSelectedIndex((prev) => Math.min(prev + 1, schemaParameters.length - 1))
|
||||
break
|
||||
return
|
||||
case 'ArrowUp':
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setSchemaParamSelectedIndex((prev) => Math.max(prev - 1, 0))
|
||||
break
|
||||
return
|
||||
case 'Enter':
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
@@ -722,14 +684,17 @@ try {
|
||||
const selectedParam = schemaParameters[schemaParamSelectedIndex]
|
||||
handleSchemaParamSelect(selectedParam.name)
|
||||
}
|
||||
break
|
||||
return
|
||||
case 'Escape':
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setShowSchemaParams(false)
|
||||
break
|
||||
return
|
||||
case ' ':
|
||||
case 'Tab':
|
||||
setShowSchemaParams(false)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (showEnvVars || showTags) {
|
||||
@@ -743,7 +708,7 @@ try {
|
||||
const handleSchemaWandClick = () => {
|
||||
if (schemaGeneration.isLoading || schemaGeneration.isStreaming) return
|
||||
setIsSchemaPromptActive(true)
|
||||
setSchemaPromptInput(schemaPromptSummary ?? '')
|
||||
setSchemaPromptInput('')
|
||||
setTimeout(() => {
|
||||
schemaPromptInputRef.current?.focus()
|
||||
}, 0)
|
||||
@@ -762,7 +727,6 @@ try {
|
||||
const handleSchemaPromptSubmit = () => {
|
||||
const trimmedPrompt = schemaPromptInput.trim()
|
||||
if (!trimmedPrompt || schemaGeneration.isLoading || schemaGeneration.isStreaming) return
|
||||
setSchemaPromptSummary(trimmedPrompt)
|
||||
schemaGeneration.generateStream({ prompt: trimmedPrompt })
|
||||
setSchemaPromptInput('')
|
||||
setIsSchemaPromptActive(false)
|
||||
@@ -782,7 +746,7 @@ try {
|
||||
const handleCodeWandClick = () => {
|
||||
if (codeGeneration.isLoading || codeGeneration.isStreaming) return
|
||||
setIsCodePromptActive(true)
|
||||
setCodePromptInput(codePromptSummary ?? '')
|
||||
setCodePromptInput('')
|
||||
setTimeout(() => {
|
||||
codePromptInputRef.current?.focus()
|
||||
}, 0)
|
||||
@@ -801,7 +765,6 @@ try {
|
||||
const handleCodePromptSubmit = () => {
|
||||
const trimmedPrompt = codePromptInput.trim()
|
||||
if (!trimmedPrompt || codeGeneration.isLoading || codeGeneration.isStreaming) return
|
||||
setCodePromptSummary(trimmedPrompt)
|
||||
codeGeneration.generateStream({ prompt: trimmedPrompt })
|
||||
setCodePromptInput('')
|
||||
setIsCodePromptActive(false)
|
||||
@@ -846,19 +809,8 @@ try {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal open={open} onOpenChange={handleClose}>
|
||||
<ModalContent
|
||||
size='xl'
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Escape' && (showEnvVars || showTags || showSchemaParams)) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setShowEnvVars(false)
|
||||
setShowTags(false)
|
||||
setShowSchemaParams(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Modal open={open} onOpenChange={handleCloseAttempt}>
|
||||
<ModalContent size='xl'>
|
||||
<ModalHeader>{isEditing ? 'Edit Agent Tool' : 'Create Agent Tool'}</ModalHeader>
|
||||
|
||||
<ModalTabs
|
||||
@@ -1211,7 +1163,7 @@ try {
|
||||
<Button
|
||||
variant='tertiary'
|
||||
onClick={handleSave}
|
||||
disabled={!isSchemaValid || !!schemaError}
|
||||
disabled={!isSchemaValid || !!schemaError || !hasChanges}
|
||||
>
|
||||
{isEditing ? 'Update Tool' : 'Save Tool'}
|
||||
</Button>
|
||||
@@ -1248,6 +1200,26 @@ try {
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
<Modal open={showDiscardAlert} onOpenChange={setShowDiscardAlert}>
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Unsaved Changes</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
You have unsaved changes to this tool. Are you sure you want to discard your changes
|
||||
and close the editor?
|
||||
</p>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant='default' onClick={() => setShowDiscardAlert(false)}>
|
||||
Keep Editing
|
||||
</Button>
|
||||
<Button variant='destructive' onClick={handleConfirmDiscard}>
|
||||
Discard Changes
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1072,7 +1072,7 @@ export function AccessControl() {
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Unsaved Changes</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
You have unsaved changes. Do you want to save them before closing?
|
||||
</p>
|
||||
</ModalBody>
|
||||
|
||||
@@ -115,7 +115,7 @@ export function CreateApiKeyModal({
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Create new API key</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
{keyType === 'workspace'
|
||||
? "This key will have access to all workflows in this workspace. Make sure to copy it after creation as you won't be able to see it again."
|
||||
: "This key will have access to your personal workflows. Make sure to copy it after creation as you won't be able to see it again."}
|
||||
@@ -218,7 +218,7 @@ export function CreateApiKeyModal({
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Your API key has been created</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
This is the only time you will see your API key.{' '}
|
||||
<span className='font-semibold text-[var(--text-primary)]'>
|
||||
Copy it now and store it securely.
|
||||
|
||||
@@ -222,7 +222,7 @@ export function BYOK() {
|
||||
)}
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
This key will be used for all {PROVIDERS.find((p) => p.id === editingProvider)?.name}{' '}
|
||||
requests in this workspace. Your key is encrypted and stored securely.
|
||||
</p>
|
||||
@@ -308,7 +308,7 @@ export function BYOK() {
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Delete API Key</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
Are you sure you want to delete the{' '}
|
||||
<span className='font-medium text-[var(--text-primary)]'>
|
||||
{PROVIDERS.find((p) => p.id === deleteConfirmProvider)?.name}
|
||||
|
||||
@@ -214,7 +214,7 @@ export function Copilot() {
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Create new API key</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
This key will allow access to Copilot features. Make sure to copy it after creation as
|
||||
you won't be able to see it again.
|
||||
</p>
|
||||
@@ -276,7 +276,7 @@ export function Copilot() {
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Your API key has been created</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
This is the only time you will see your API key.{' '}
|
||||
<span className='font-semibold text-[var(--text-primary)]'>
|
||||
Copy it now and store it securely.
|
||||
|
||||
@@ -824,7 +824,7 @@ export function EnvironmentVariables({ registerBeforeLeaveHandler }: Environment
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Unsaved Changes</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
{hasConflicts || hasInvalidKeys
|
||||
? `You have unsaved changes, but ${hasConflicts ? 'conflicts must be resolved' : 'invalid variable names must be fixed'} before saving. You can discard your changes to close the modal.`
|
||||
: 'You have unsaved changes. Do you want to save them before closing?'}
|
||||
|
||||
@@ -603,7 +603,7 @@ export function General({ onOpenChange }: GeneralProps) {
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Reset Password</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>
|
||||
A password reset link will be sent to{' '}
|
||||
<span className='font-medium text-[var(--text-primary)]'>{profile?.email}</span>.
|
||||
Click the link in the email to create a new password.
|
||||
|
||||
@@ -64,7 +64,7 @@ export function TeamSeats({
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>{title}</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-muted)]'>{description}</p>
|
||||
<p className='text-[12px] text-[var(--text-secondary)]'>{description}</p>
|
||||
|
||||
<div className='mt-[16px] flex flex-col gap-[4px]'>
|
||||
<Label htmlFor='seats' className='text-[12px]'>
|
||||
|
||||
@@ -25,9 +25,11 @@ const GRID_COLUMNS = 6
|
||||
function ColorGrid({
|
||||
hexInput,
|
||||
setHexInput,
|
||||
onColorChange,
|
||||
}: {
|
||||
hexInput: string
|
||||
setHexInput: (color: string) => void
|
||||
onColorChange?: (color: string) => void
|
||||
}) {
|
||||
const { isInFolder } = usePopoverContext()
|
||||
const [focusedIndex, setFocusedIndex] = useState(-1)
|
||||
@@ -72,7 +74,9 @@ function ColorGrid({
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setHexInput(WORKFLOW_COLORS[index].color)
|
||||
onColorChange?.(WORKFLOW_COLORS[index].color)
|
||||
return
|
||||
default:
|
||||
return
|
||||
@@ -83,7 +87,7 @@ function ColorGrid({
|
||||
buttonRefs.current[newIndex]?.focus()
|
||||
}
|
||||
},
|
||||
[setHexInput]
|
||||
[setHexInput, onColorChange]
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -105,8 +109,10 @@ function ColorGrid({
|
||||
onKeyDown={(e) => handleKeyDown(e, index)}
|
||||
onFocus={() => setFocusedIndex(index)}
|
||||
className={cn(
|
||||
'h-[20px] w-[20px] rounded-[4px] focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-1 focus:ring-offset-[#1b1b1b]',
|
||||
hexInput.toLowerCase() === color.toLowerCase() && 'ring-1 ring-white'
|
||||
'h-[20px] w-[20px] rounded-[4px] outline-none ring-white ring-offset-0',
|
||||
(focusedIndex === index ||
|
||||
(focusedIndex === -1 && hexInput.toLowerCase() === color.toLowerCase())) &&
|
||||
'ring-[1.5px]'
|
||||
)}
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
@@ -450,7 +456,11 @@ export function ContextMenu({
|
||||
>
|
||||
<div className='flex w-[140px] flex-col gap-[8px] p-[2px]'>
|
||||
{/* Preset colors with keyboard navigation */}
|
||||
<ColorGrid hexInput={hexInput} setHexInput={setHexInput} />
|
||||
<ColorGrid
|
||||
hexInput={hexInput}
|
||||
setHexInput={setHexInput}
|
||||
onColorChange={onColorChange}
|
||||
/>
|
||||
|
||||
{/* Hex input */}
|
||||
<div className='flex items-center gap-[4px]'>
|
||||
|
||||
@@ -459,6 +459,7 @@ export function WorkspaceHeader({
|
||||
value={editingName}
|
||||
onChange={(e) => setEditingName(e.target.value)}
|
||||
onKeyDown={async (e) => {
|
||||
e.stopPropagation()
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
setIsListRenaming(true)
|
||||
|
||||
@@ -460,6 +460,13 @@ const PopoverContent = React.forwardRef<
|
||||
const content = contentRef.current
|
||||
if (!content) return
|
||||
|
||||
const activeElement = document.activeElement
|
||||
const isInputFocused =
|
||||
activeElement instanceof HTMLInputElement ||
|
||||
activeElement instanceof HTMLTextAreaElement ||
|
||||
activeElement?.getAttribute('contenteditable') === 'true'
|
||||
if (isInputFocused) return
|
||||
|
||||
const items = content.querySelectorAll<HTMLElement>(
|
||||
'[role="menuitem"]:not([aria-disabled="true"])'
|
||||
)
|
||||
|
||||
@@ -755,12 +755,11 @@ export interface BulkChunkOperationParams {
|
||||
}
|
||||
|
||||
export interface BulkChunkOperationResult {
|
||||
operation: string
|
||||
successCount: number
|
||||
failedCount: number
|
||||
results: Array<{
|
||||
operation: string
|
||||
chunkIds: string[]
|
||||
}>
|
||||
errorCount: number
|
||||
processed: number
|
||||
errors: string[]
|
||||
}
|
||||
|
||||
export async function bulkChunkOperation({
|
||||
|
||||
Reference in New Issue
Block a user