Skip to content

Commit cbe63a3

Browse files
committed
improvement: subblocks
1 parent c3ef98d commit cbe63a3

File tree

23 files changed

+482
-153
lines changed

23 files changed

+482
-153
lines changed

apps/sim/app/_styles/globals.css

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -576,32 +576,6 @@ input[type="search"]::-ms-clear {
576576
transition-duration: 300ms;
577577
}
578578

579-
.streaming-effect {
580-
@apply relative overflow-hidden;
581-
}
582-
583-
.streaming-effect::after {
584-
content: "";
585-
@apply pointer-events-none absolute left-0 top-0 h-full w-full;
586-
background: linear-gradient(
587-
90deg,
588-
rgba(128, 128, 128, 0) 0%,
589-
rgba(128, 128, 128, 0.1) 50%,
590-
rgba(128, 128, 128, 0) 100%
591-
);
592-
animation: code-shimmer 1.5s infinite;
593-
z-index: 10;
594-
}
595-
596-
.dark .streaming-effect::after {
597-
background: linear-gradient(
598-
90deg,
599-
rgba(180, 180, 180, 0) 0%,
600-
rgba(180, 180, 180, 0.1) 50%,
601-
rgba(180, 180, 180, 0) 100%
602-
);
603-
}
604-
605579
.loading-placeholder::placeholder {
606580
animation: placeholder-pulse 1.5s ease-in-out infinite;
607581
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/code/code.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,10 @@ export function Code({
336336
setCode('')
337337
}
338338

339+
handleStreamChunkRef.current = (chunk: string) => {
340+
setCode((prev) => prev + chunk)
341+
}
342+
339343
handleGeneratedContentRef.current = (generatedCode: string) => {
340344
setCode(generatedCode)
341345
if (!isPreview && !disabled) {
@@ -691,11 +695,7 @@ export function Code({
691695
/>
692696
)}
693697

694-
<CodeEditor.Container
695-
onDragOver={(e) => e.preventDefault()}
696-
onDrop={handleDrop}
697-
isStreaming={isAiStreaming}
698-
>
698+
<CodeEditor.Container onDragOver={(e) => e.preventDefault()} onDrop={handleDrop}>
699699
<div className='absolute top-2 right-3 z-10 flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100'>
700700
{wandConfig?.enabled &&
701701
!isAiStreaming &&
@@ -761,6 +761,11 @@ export function Code({
761761
}}
762762
onFocus={() => {
763763
hasEditedSinceFocusRef.current = false
764+
// Show tag dropdown on focus when code is empty
765+
if (!isPreview && !disabled && !readOnly && code.trim() === '') {
766+
setShowTags(true)
767+
setCursorPosition(0)
768+
}
764769
}}
765770
highlight={createHighlightFunction(effectiveLanguage, shouldHighlightReference)}
766771
{...getCodeEditorProps({ isStreaming: isAiStreaming, isPreview, disabled })}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/condition-input/condition-input.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export function ConditionInput({
115115
const accessiblePrefixes = useAccessibleReferencePrefixes(blockId)
116116

117117
const containerRef = useRef<HTMLDivElement>(null)
118+
const inputRefs = useRef<Map<string, HTMLTextAreaElement>>(new Map())
118119

119120
/**
120121
* Determines if a reference string should be highlighted in the editor.
@@ -728,6 +729,20 @@ export function ConditionInput({
728729
})
729730
}, [conditionalBlocks.length])
730731

732+
// Capture textarea refs from Editor components (condition mode)
733+
useEffect(() => {
734+
if (!isRouterMode && containerRef.current) {
735+
conditionalBlocks.forEach((block) => {
736+
const textarea = containerRef.current?.querySelector(
737+
`[data-block-id="${block.id}"] textarea`
738+
) as HTMLTextAreaElement | null
739+
if (textarea) {
740+
inputRefs.current.set(block.id, textarea)
741+
}
742+
})
743+
}
744+
}, [conditionalBlocks, isRouterMode])
745+
731746
// Show loading or empty state if not ready or no blocks
732747
if (!isReady || conditionalBlocks.length === 0) {
733748
return (
@@ -842,6 +857,9 @@ export function ConditionInput({
842857
onDrop={(e) => handleDrop(block.id, e)}
843858
>
844859
<Textarea
860+
ref={(el) => {
861+
if (el) inputRefs.current.set(block.id, el)
862+
}}
845863
data-router-block-id={block.id}
846864
value={block.value}
847865
onChange={(e) => {
@@ -869,6 +887,15 @@ export function ConditionInput({
869887
)
870888
}
871889
}}
890+
onFocus={() => {
891+
if (!isPreview && !disabled && block.value.trim() === '') {
892+
setConditionalBlocks((blocks) =>
893+
blocks.map((b) =>
894+
b.id === block.id ? { ...b, showTags: true, cursorPosition: 0 } : b
895+
)
896+
)
897+
}
898+
}}
872899
onBlur={() => {
873900
setTimeout(() => {
874901
setConditionalBlocks((blocks) =>
@@ -929,6 +956,11 @@ export function ConditionInput({
929956
)
930957
)
931958
}}
959+
inputRef={
960+
{
961+
current: inputRefs.current.get(block.id) || null,
962+
} as React.RefObject<HTMLTextAreaElement>
963+
}
932964
/>
933965
)}
934966
</div>
@@ -1006,6 +1038,15 @@ export function ConditionInput({
10061038
)
10071039
}
10081040
}}
1041+
onFocus={() => {
1042+
if (!isPreview && !disabled && block.value.trim() === '') {
1043+
setConditionalBlocks((blocks) =>
1044+
blocks.map((b) =>
1045+
b.id === block.id ? { ...b, showTags: true, cursorPosition: 0 } : b
1046+
)
1047+
)
1048+
}
1049+
}}
10091050
highlight={(codeToHighlight) => {
10101051
const placeholders: {
10111052
placeholder: string
@@ -1113,6 +1154,11 @@ export function ConditionInput({
11131154
)
11141155
)
11151156
}}
1157+
inputRef={
1158+
{
1159+
current: inputRefs.current.get(block.id) || null,
1160+
} as React.RefObject<HTMLTextAreaElement>
1161+
}
11161162
/>
11171163
)}
11181164
</div>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ export function DocumentTagEntry({
288288
onKeyDown={handlers.onKeyDown}
289289
onDrop={handlers.onDrop}
290290
onDragOver={handlers.onDragOver}
291+
onFocus={handlers.onFocus}
291292
onScroll={(e) => syncOverlayScroll(cellKey, e.currentTarget.scrollLeft)}
292293
onPaste={() =>
293294
setTimeout(() => {

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ export function EvalInput({
214214
onKeyDown={handlers.onKeyDown}
215215
onDrop={handlers.onDrop}
216216
onDragOver={handlers.onDragOver}
217+
onFocus={handlers.onFocus}
217218
placeholder='How accurate is the response?'
218219
disabled={isPreview || disabled}
219220
className={cn(

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/input-mapping/input-mapping.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ function InputMappingField({
237237
onKeyDown={handlers.onKeyDown}
238238
onDrop={handlers.onDrop}
239239
onDragOver={handlers.onDragOver}
240+
onFocus={handlers.onFocus}
240241
onScroll={(e) => handleScroll(e)}
241242
onPaste={() =>
242243
setTimeout(() => {

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ export function KnowledgeTagFilters({
276276
onKeyDown={handlers.onKeyDown}
277277
onDrop={handlers.onDrop}
278278
onDragOver={handlers.onDragOver}
279+
onFocus={handlers.onFocus}
279280
onScroll={(e) => syncOverlayScroll(cellKey, e.currentTarget.scrollLeft)}
280281
onPaste={() =>
281282
setTimeout(() => {

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type React from 'react'
12
import {
23
useCallback,
34
useEffect,
@@ -159,6 +160,27 @@ export function LongInput({
159160

160161
const accessiblePrefixes = useAccessibleReferencePrefixes(blockId)
161162

163+
/**
164+
* Callback to show tag dropdown when input is empty and focused
165+
*/
166+
const shouldForceTagDropdown = useCallback(
167+
({
168+
value,
169+
}: {
170+
value: string
171+
cursor: number
172+
event: 'focus'
173+
}): { show: boolean } | undefined => {
174+
if (isPreview || disabled) return { show: false }
175+
// Show tag dropdown on focus when input is empty
176+
if (value.trim() === '') {
177+
return { show: true }
178+
}
179+
return { show: false }
180+
},
181+
[isPreview, disabled]
182+
)
183+
162184
// During streaming, use local content; otherwise use the controller value
163185
const value = useMemo(() => {
164186
if (wandHook.isStreaming) return localContent
@@ -294,6 +316,7 @@ export function LongInput({
294316
disabled={disabled}
295317
isStreaming={wandHook.isStreaming}
296318
previewValue={previewValue}
319+
shouldForceTagDropdown={shouldForceTagDropdown}
297320
>
298321
{({ ref, onChange: handleChange, onKeyDown, onDrop, onDragOver, onFocus }) => {
299322
const setRefs = (el: HTMLTextAreaElement | null) => {
@@ -303,7 +326,7 @@ export function LongInput({
303326
return (
304327
<div
305328
ref={containerRef}
306-
className={cn('group relative w-full', wandHook.isStreaming && 'streaming-effect')}
329+
className='group relative w-full'
307330
style={{ height: `${height}px` }}
308331
>
309332
<Textarea

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-dynamic-args/mcp-dynamic-args.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,14 @@ function McpInputWithTags({
111111
data-lpignore='true'
112112
data-1p-ignore
113113
readOnly
114-
onFocus={(e) => e.currentTarget.removeAttribute('readOnly')}
114+
onFocus={(e) => {
115+
e.currentTarget.removeAttribute('readOnly')
116+
// Show tag dropdown on focus when input is empty
117+
if (!disabled && (value?.trim() === '' || !value)) {
118+
setShowTags(true)
119+
setCursorPosition(0)
120+
}
121+
}}
115122
className={cn(!isPassword && 'text-transparent caret-foreground')}
116123
/>
117124
{!isPassword && (
@@ -136,6 +143,7 @@ function McpInputWithTags({
136143
setShowTags(false)
137144
setActiveSourceBlockId(null)
138145
}}
146+
inputRef={inputRef}
139147
/>
140148
</div>
141149
)
@@ -225,6 +233,13 @@ function McpTextareaWithTags({
225233
onChange={handleChange}
226234
onDrop={handleDrop}
227235
onDragOver={handleDragOver}
236+
onFocus={() => {
237+
// Show tag dropdown on focus when input is empty
238+
if (!disabled && (value?.trim() === '' || !value)) {
239+
setShowTags(true)
240+
setCursorPosition(0)
241+
}
242+
}}
228243
placeholder={placeholder}
229244
disabled={disabled}
230245
rows={rows}
@@ -254,6 +269,7 @@ function McpTextareaWithTags({
254269
setShowTags(false)
255270
setActiveSourceBlockId(null)
256271
}}
272+
inputRef={textareaRef}
257273
/>
258274
</div>
259275
)

0 commit comments

Comments
 (0)