Skip to content

Commit bfd1997

Browse files
committed
fix: supabase, dropdown, short-input, tool execution
1 parent 24e9a1b commit bfd1997

File tree

11 files changed

+353
-384
lines changed

11 files changed

+353
-384
lines changed

sim/app/w/[id]/components/workflow-block/components/sub-block/components/dropdown.tsx

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMemo } from 'react'
1+
import { useEffect, useMemo, useState } from 'react'
22
import {
33
Select,
44
SelectContent,
@@ -19,6 +19,7 @@ interface DropdownProps {
1919

2020
export function Dropdown({ options, defaultValue, blockId, subBlockId }: DropdownProps) {
2121
const [value, setValue] = useSubBlockValue<string>(blockId, subBlockId, true)
22+
const [storeInitialized, setStoreInitialized] = useState(false)
2223

2324
// Evaluate options if it's a function
2425
const evaluatedOptions = useMemo(() => {
@@ -33,33 +34,57 @@ export function Dropdown({ options, defaultValue, blockId, subBlockId }: Dropdow
3334
return typeof option === 'string' ? option : option.label
3435
}
3536

37+
// Get the default option value (first option or provided defaultValue)
38+
const defaultOptionValue = useMemo(() => {
39+
if (defaultValue !== undefined) {
40+
return defaultValue
41+
}
42+
43+
if (evaluatedOptions.length > 0) {
44+
return getOptionValue(evaluatedOptions[0])
45+
}
46+
47+
return undefined
48+
}, [defaultValue, evaluatedOptions, getOptionValue])
49+
50+
// Mark store as initialized on first render
51+
useEffect(() => {
52+
setStoreInitialized(true)
53+
}, [])
54+
55+
// Only set default value once the store is confirmed to be initialized
56+
// and we know the actual value is null/undefined (not just loading)
57+
useEffect(() => {
58+
if (
59+
storeInitialized &&
60+
(value === null || value === undefined) &&
61+
defaultOptionValue !== undefined
62+
) {
63+
setValue(defaultOptionValue)
64+
}
65+
}, [storeInitialized, value, defaultOptionValue, setValue])
66+
3667
// Calculate the effective value to use in the dropdown
37-
// Priority: 1. Stored value (value) > 2. Provided defaultValue > 3. First option
3868
const effectiveValue = useMemo(() => {
3969
// If we have a value from the store, use that
4070
if (value !== null && value !== undefined) {
4171
return value
4272
}
4373

44-
// Fall back to provided defaultValue
45-
if (defaultValue !== undefined) {
46-
return defaultValue
47-
}
48-
49-
// Last resort: use first option value if available
50-
if (evaluatedOptions.length > 0) {
51-
return getOptionValue(evaluatedOptions[0])
74+
// Only return defaultOptionValue if store is initialized
75+
if (storeInitialized) {
76+
return defaultOptionValue
5277
}
5378

54-
// No valid value available
79+
// While store is loading, don't use any value
5580
return undefined
56-
}, [value, defaultValue, evaluatedOptions])
81+
}, [value, defaultOptionValue, storeInitialized])
5782

5883
// Handle the case where evaluatedOptions changes and the current selection is no longer valid
5984
const isValueInOptions = useMemo(() => {
6085
if (!effectiveValue || evaluatedOptions.length === 0) return false
6186
return evaluatedOptions.some((opt) => getOptionValue(opt) === effectiveValue)
62-
}, [effectiveValue, evaluatedOptions])
87+
}, [effectiveValue, evaluatedOptions, getOptionValue])
6388

6489
return (
6590
<Select

sim/app/w/[id]/components/workflow-block/components/sub-block/components/short-input.tsx

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,23 @@ export function ShortInput({
119119
}
120120
}
121121

122-
// Update the auto-scroll effect to handle both input and overlay
122+
// Remove the auto-scroll effect that forces cursor position and replace with natural scrolling
123123
useEffect(() => {
124-
if (inputRef.current && isFocused) {
125-
const input = inputRef.current
126-
const scrollPosition = (input.selectionStart ?? 0) * 8
127-
input.scrollLeft = scrollPosition - input.offsetWidth / 2
128-
129-
if (overlayRef.current) {
130-
overlayRef.current.scrollLeft = input.scrollLeft
131-
}
124+
if (inputRef.current && overlayRef.current) {
125+
overlayRef.current.scrollLeft = inputRef.current.scrollLeft
132126
}
133-
}, [value, isFocused])
127+
}, [value])
128+
129+
// Handle paste events to ensure long values are handled correctly
130+
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
131+
// Let the paste happen normally
132+
// Then ensure scroll positions are synced after the content is updated
133+
setTimeout(() => {
134+
if (inputRef.current && overlayRef.current) {
135+
overlayRef.current.scrollLeft = inputRef.current.scrollLeft
136+
}
137+
}, 0)
138+
}
134139

135140
// Handle wheel events to control ReactFlow zoom
136141
const handleWheel = (e: React.WheelEvent<HTMLInputElement>) => {
@@ -176,9 +181,8 @@ export function ShortInput({
176181
}
177182

178183
// For regular scrolling (without Ctrl/Cmd), let the default behavior happen
179-
if (overlayRef.current) {
180-
overlayRef.current.scrollLeft = e.currentTarget.scrollLeft
181-
}
184+
// Don't interfere with normal scrolling
185+
return true
182186
}
183187

184188
// Drag and Drop handlers
@@ -271,7 +275,7 @@ export function ShortInput({
271275
<Input
272276
ref={inputRef}
273277
className={cn(
274-
'w-full placeholder:text-muted-foreground/50 allow-scroll text-transparent caret-foreground',
278+
'w-full placeholder:text-muted-foreground/50 allow-scroll text-transparent caret-foreground overflow-auto',
275279
isConnecting &&
276280
config?.connectionDroppable !== false &&
277281
'focus-visible:ring-blue-500 ring-2 ring-blue-500 ring-offset-2'
@@ -304,17 +308,25 @@ export function ShortInput({
304308
onDrop={handleDrop}
305309
onDragOver={handleDragOver}
306310
onScroll={handleScroll}
311+
onPaste={handlePaste}
307312
onWheel={handleWheel}
308313
onKeyDown={handleKeyDown}
309314
autoComplete="off"
315+
style={{ overflowX: 'auto' }}
310316
/>
311317
<div
312318
ref={overlayRef}
313-
className="absolute inset-0 pointer-events-none px-3 flex items-center overflow-x-auto whitespace-pre text-sm bg-transparent"
319+
className="absolute inset-0 pointer-events-none px-3 flex items-center overflow-x-auto text-sm bg-transparent"
320+
style={{ overflowX: 'auto' }}
314321
>
315-
{password && !isFocused
316-
? '•'.repeat(value?.toString().length ?? 0)
317-
: formatDisplayText(value?.toString() ?? '')}
322+
<div
323+
className="whitespace-pre w-full"
324+
style={{ scrollbarWidth: 'none', minWidth: 'fit-content' }}
325+
>
326+
{password && !isFocused
327+
? '•'.repeat(value?.toString().length ?? 0)
328+
: formatDisplayText(value?.toString() ?? '')}
329+
</div>
318330
</div>
319331
<EnvVarDropdown
320332
visible={showEnvVars}

sim/blocks/blocks/supabase.ts

Lines changed: 26 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
1212
name: 'Supabase',
1313
description: 'Use Supabase database',
1414
longDescription:
15-
'Integrate with Supabase to manage your database, authentication, storage, and more. Query data, manage users, and interact with Supabase services using OAuth authentication.',
15+
'Integrate with Supabase to manage your database, authentication, storage, and more. Query data, manage users, and interact with Supabase services directly.',
1616
category: 'tools',
1717
bgColor: '#1C1C1C',
1818
icon: SupabaseIcon,
@@ -24,29 +24,17 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
2424
type: 'dropdown',
2525
layout: 'full',
2626
options: [
27-
{ label: 'Query Data', id: 'query' },
28-
{ label: 'Insert Data', id: 'insert' },
29-
{ label: 'Update Data', id: 'update' },
27+
{ label: 'Read All Rows', id: 'query' },
28+
{ label: 'Insert Rows', id: 'insert' },
3029
],
3130
},
32-
// Supabase Credentials
33-
{
34-
id: 'credential',
35-
title: 'Supabase Account',
36-
type: 'oauth-input',
37-
layout: 'full',
38-
provider: 'supabase',
39-
serviceId: 'supabase',
40-
requiredScopes: ['database.read', 'database.write', 'projects.read'],
41-
placeholder: 'Select Supabase account',
42-
},
4331
// Common Fields
4432
{
4533
id: 'projectId',
4634
title: 'Project ID',
4735
type: 'short-input',
4836
layout: 'full',
49-
placeholder: 'ID of the Supabase project',
37+
placeholder: 'Your Supabase project ID (e.g., jdrkgepadsdopsntdlom)',
5038
},
5139
{
5240
id: 'table',
@@ -55,94 +43,65 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
5543
layout: 'full',
5644
placeholder: 'Name of the table',
5745
},
58-
// Query-specific Fields
5946
{
60-
id: 'select',
61-
title: 'Select',
47+
id: 'apiKey',
48+
title: 'Client Anon Key',
6249
type: 'short-input',
6350
layout: 'full',
64-
placeholder: 'Columns to select (e.g., id, name, email)',
65-
condition: { field: 'operation', value: 'query' },
66-
},
67-
{
68-
id: 'filter',
69-
title: 'Filter',
70-
type: 'long-input',
71-
layout: 'full',
72-
placeholder:
73-
'Filter conditions as JSON (e.g., {"column": "name", "operator": "eq", "value": "John"})',
74-
condition: { field: 'operation', value: 'query' },
51+
placeholder: 'Your Supabase client anon key',
52+
password: true,
7553
},
76-
// Insert/Update-specific Fields
54+
// Insert-specific Fields
7755
{
7856
id: 'data',
7957
title: 'Data',
80-
type: 'long-input',
58+
type: 'code',
8159
layout: 'full',
82-
placeholder:
83-
'Data to insert/update as JSON (e.g., {"name": "John", "email": "john@example.com"})',
60+
placeholder: '{\n "column1": "value1",\n "column2": "value2"\n}',
8461
condition: { field: 'operation', value: 'insert' },
8562
},
86-
{
87-
id: 'data',
88-
title: 'Data',
89-
type: 'long-input',
90-
layout: 'full',
91-
placeholder:
92-
'Data to insert/update as JSON (e.g., {"name": "John", "email": "john@example.com"})',
93-
condition: { field: 'operation', value: 'update' },
94-
},
95-
// Update-specific Fields
96-
{
97-
id: 'filter',
98-
title: 'Filter',
99-
type: 'long-input',
100-
layout: 'full',
101-
placeholder:
102-
'Filter conditions as JSON (e.g., {"column": "id", "operator": "eq", "value": 123})',
103-
condition: { field: 'operation', value: 'update' },
104-
},
10563
],
10664
tools: {
107-
access: ['supabase_query', 'supabase_insert', 'supabase_update'],
65+
access: ['supabase_query', 'supabase_insert'],
10866
config: {
10967
tool: (params) => {
11068
switch (params.operation) {
11169
case 'query':
11270
return 'supabase_query'
11371
case 'insert':
11472
return 'supabase_insert'
115-
case 'update':
116-
return 'supabase_update'
11773
default:
11874
throw new Error(`Invalid Supabase operation: ${params.operation}`)
11975
}
12076
},
12177
params: (params) => {
122-
const { credential, data, filter, ...rest } = params
78+
const { data, ...rest } = params
12379

124-
// Parse JSON strings to objects if they exist
125-
const parsedData = data ? JSON.parse(data as string) : undefined
126-
const parsedFilter = filter ? JSON.parse(filter as string) : undefined
80+
// Parse JSON data if it's a string
81+
let parsedData;
82+
if (data && typeof data === 'string') {
83+
try {
84+
parsedData = JSON.parse(data);
85+
} catch (e) {
86+
throw new Error('Invalid JSON data format');
87+
}
88+
} else {
89+
parsedData = data;
90+
}
12791

12892
return {
12993
...rest,
13094
data: parsedData,
131-
filter: parsedFilter,
132-
credential,
13395
}
13496
},
13597
},
13698
},
13799
inputs: {
138100
operation: { type: 'string', required: true },
139-
credential: { type: 'string', required: true },
140101
projectId: { type: 'string', required: true },
141102
table: { type: 'string', required: true },
142-
// Query operation inputs
143-
select: { type: 'string', required: false },
144-
filter: { type: 'string', required: false },
145-
// Insert/Update operation inputs
103+
apiKey: { type: 'string', required: true },
104+
// Insert operation inputs
146105
data: { type: 'string', required: false },
147106
},
148107
outputs: {

sim/blocks/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { SerperBlock } from './blocks/serper'
2727
import { GoogleSheetsBlock } from './blocks/sheets'
2828
import { SlackBlock } from './blocks/slack'
2929
import { StarterBlock } from './blocks/starter'
30-
// import { SupabaseBlock } from './blocks/supabase'
30+
import { SupabaseBlock } from './blocks/supabase'
3131
import { TavilyBlock } from './blocks/tavily'
3232
import { TranslateBlock } from './blocks/translate'
3333
import { TwilioSMSBlock } from './blocks/twilio'
@@ -61,7 +61,7 @@ export {
6161
YouTubeBlock,
6262
NotionBlock,
6363
GmailBlock,
64-
// SupabaseBlock,
64+
SupabaseBlock,
6565
XBlock,
6666
StarterBlock,
6767
PineconeBlock,
@@ -109,7 +109,7 @@ const blocks: Record<string, BlockConfig> = {
109109
serper: SerperBlock,
110110
slack: SlackBlock,
111111
starter: StarterBlock,
112-
// supabase: SupabaseBlock,
112+
supabase: SupabaseBlock,
113113
tavily: TavilyBlock,
114114
translate: TranslateBlock,
115115
twilio_sms: TwilioSMSBlock,

0 commit comments

Comments
 (0)