+S3 Object editor bar

This commit is contained in:
Diego Imbert
2025-06-27 14:06:32 +02:00
parent b9cd037a9b
commit 2f13eb4eb8
4 changed files with 54 additions and 3 deletions
+44 -3
View File
@@ -29,6 +29,7 @@
import {
DiffIcon,
DollarSign,
File,
History,
Library,
Link,
@@ -38,7 +39,7 @@
Save,
Users
} from 'lucide-svelte'
import { capitalize, toCamel } from '$lib/utils'
import { capitalize, formatS3Object, toCamel } from '$lib/utils'
import type { Schema, SchemaProperty, SupportedLanguage } from '$lib/common'
import ScriptVersionHistory from './ScriptVersionHistory.svelte'
import ScriptGen from './copilot/ScriptGen.svelte'
@@ -48,6 +49,7 @@
import ResourceEditorDrawer from './ResourceEditorDrawer.svelte'
import type { EditorBarUi } from './custom_ui'
import EditorSettings from './EditorSettings.svelte'
import S3FilePicker from './S3FilePicker.svelte'
interface Props {
lang: SupportedLanguage | 'bunnative' | undefined
@@ -109,6 +111,7 @@
let resourceTypePicker: ItemPicker | undefined = $state()
let variableEditor: VariableEditor | undefined = $state()
let resourceEditor: ResourceEditorDrawer | undefined = $state()
let s3FilePicker: S3FilePicker | undefined = $state()
let showContextVarPicker = $derived(
[
@@ -168,6 +171,8 @@
].includes(lang ?? '')
)
let showS3Picker = $derived(['duckdb'].includes(lang ?? ''))
let showResourceTypePicker = $derived(
['typescript', 'javascript'].includes(scriptLangToEditorLang(lang)) ||
lang === 'python3' ||
@@ -582,8 +587,14 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
editor.insertAtCursor(`(Wmill.getResource("${path}"))`)
// for related places search: ADD_NEW_LANG
} else if (lang == 'duckdb') {
let t = resType == 'postgresql' ? 'postgres' : resType
editor.insertAtCursor(`ATTACH '$res:${path}' AS db (TYPE ${t});`)
let t = { postgresql: 'postgres', mysql: 'mysql', bigquery: 'bigquery' }[resType]
if (!t) {
sendUserToast(`Resource type ${resType} is not supported in DuckDB`, true)
editor.insertAtCursor(`'$res:${path}'`)
return
} else {
editor.insertAtCursor(`ATTACH '$res:${path}' AS db (TYPE ${t});`)
}
}
sendUserToast(`${path} inserted at cursor`)
@@ -630,6 +641,20 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
<ResourceEditorDrawer bind:this={resourceEditor} on:refresh={resourcePicker.openDrawer} />
<VariableEditor bind:this={variableEditor} on:create={variablePicker.openDrawer} />
<S3FilePicker
bind:this={s3FilePicker}
readOnlyMode={false}
on:selectAndClose={(s3obj) => {
if (lang === 'duckdb') {
let s = `'${formatS3Object(s3obj.detail)}'`
if (s3obj.detail?.s3.endsWith('.json')) s = `read_json(${s})`
if (s3obj.detail?.s3.endsWith('.csv')) s = `read_csv(${s})`
if (s3obj.detail?.s3.endsWith('.parquet')) s = `read_parquet(${s})`
editor?.insertAtCursor(s)
}
}}
/>
<div class="flex justify-between items-center overflow-y-auto w-full p-0.5">
<div class="flex items-center">
<div
@@ -686,6 +711,22 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
</Button>
{/if}
{#if showS3Picker && customUi?.s3object != false}
<Button
aiId="editor-bar-add-s3-object"
aiDescription="Add S3 Object"
title="Add S3 object"
color="light"
on:click={() => s3FilePicker?.open()}
size="xs"
btnClasses="!font-medium text-tertiary"
spacingSize="md"
startIcon={{ icon: File }}
{iconOnly}
>+S3 Object
</Button>
{/if}
{#if showResourceTypePicker && customUi?.type != false}
<Button
aiId="editor-bar-add-resource-type"
@@ -72,6 +72,7 @@
let dispatch = createEventDispatcher<{
close: { s3: string; storage: string | undefined } | undefined
selectAndClose: { s3: string; storage: string | undefined }
}>()
let drawer: Drawer | undefined = $state()
@@ -423,6 +424,9 @@
}
async function selectAndClose() {
if (selectedFileKey?.s3) {
dispatch('selectAndClose', { s3: selectedFileKey.s3, storage })
}
drawer?.closeDrawer?.()
}
+1
View File
@@ -65,6 +65,7 @@ export type EditorBarUi = {
library?: boolean
useVsCode?: boolean
diffMode?: boolean
s3object?: boolean
}
export type EditableSchemaFormUi = {
+5
View File
@@ -1454,6 +1454,11 @@ export function parseS3Object(s3Object: S3Object): { s3: string; storage?: strin
return { storage: match?.[1] || undefined, s3: match?.[2] ?? '' }
}
export function formatS3Object(s3Object: S3Object): S3Uri {
if (typeof s3Object === 'object') return `s3://${s3Object.storage ?? ''}/${s3Object.s3}`
return s3Object
}
export function isS3Uri(uri: string): uri is S3Uri {
const match = uri.match(/^s3:\/\/([^/]*)\/(.*)$/)
return !!match && match.length === 3