Files
windmill/frontend/src/lib/components/S3FilePicker.svelte
T
GuilhemandClaude Opus 4.8 c000bbca28 fix(frontend): scope raw-app, flow and script editors to the session workspace (#10015)
* fix(frontend): scope raw-app/flow/script editors to the session workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): scope flow and script editor operations to the session workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): scope flow preview, inline-script creation and datatable schema to the session workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review — thread session workspace through flow resource pickers, script fetch, preview cancel/recording and path collision check

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Claude review — pass session workspace to preview FlowStatusViewer and align FlowChatManager guards

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Pi review — show acting workspace in script-not-found message and fetch picked script from it in EditorBar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 2 — thread session workspace into flow step test, raw-app inline runnable, inline editor toolbars and MCP OAuth path

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 3 — thread session workspace into dynamic-input helpers and the flow-preview argument side panel (history/saved-inputs/captures)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 4 — thread session workspace into nested flow/script drawers, flow chat inputs and the flow input side tabs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 5 — thread session workspace into script-module fork/reload and key the raw-app schema cache by workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 6 — key the DB manager schema cache by acting workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 7 — thread session workspace into resource-valued arg pickers and the editor variable/resource helper drawers

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): scope the flow asset explorer's ResourceEditorDrawer to the acting workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: thread acting workspace through flow asset explore controls

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: thread acting workspace through SQL REPL, secret args, helper forms, S3 inputs, saved inputs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 01:53:43 +02:00

159 lines
4.3 KiB
Svelte

<script lang="ts">
import { emptyString, type S3Object } from '$lib/utils'
import { Button, Drawer } from './common'
import DrawerContent from './common/drawer/DrawerContent.svelte'
import { tick } from 'svelte'
import S3FilePickerInner from './S3FilePickerInner.svelte'
import Select from './select/Select.svelte'
import { FileUp } from 'lucide-svelte'
import { SettingService } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import { resource } from 'runed'
interface Props {
fromWorkspaceSettings?: boolean
readOnlyMode: boolean
allowDelete?: boolean
initialFileKey?: { s3: string; storage?: string } | undefined
selectedFileKey?: { s3: string; storage?: string } | undefined
folderOnly?: boolean
regexFilter?: RegExp | undefined
/** Workspace to browse S3 storage in — the acting workspace of the editor that
* opened the picker, else the nav workspace. */
workspace?: string | undefined
onClose?: () => void
onSelectAndClose?: (selected: { s3: string; storage: string | undefined }) => void
}
let {
fromWorkspaceSettings = false,
readOnlyMode,
allowDelete = false,
initialFileKey = $bindable(undefined),
selectedFileKey = $bindable(undefined),
folderOnly = false,
regexFilter = undefined,
workspace = undefined,
onClose,
onSelectAndClose
}: Props = $props()
let ws = $derived(workspace ?? $workspaceStore)
let drawer: Drawer | undefined = $state()
let s3FilePickerInner: S3FilePickerInner | undefined = $state()
let workspaceSettingsInitialized = $state(true)
let storage: string | undefined = $state(undefined)
let uploadModalOpen = $state(false)
let allFilesByKey: Record<
string,
{
type: 'folder' | 'leaf'
full_key: string
display_name: string
collapsed: boolean
parentPath: string | undefined
nestingLevel: number
count: number
}
> = $state({})
let secondaryStorageNames = resource(
() => ws,
() => SettingService.getSecondaryStorageNames({ workspace: ws! }),
{ lazy: true }
)
export async function open(_preSelectedFileKey: S3Object | undefined = undefined) {
secondaryStorageNames.refetch()
drawer?.openDrawer?.()
await tick()
s3FilePickerInner?.open?.(_preSelectedFileKey)
}
</script>
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<Drawer
bind:this={drawer}
on:close={() => {
onClose?.()
s3FilePickerInner?.close?.()
}}
size="1200px"
>
<DrawerContent
title="S3 file browser"
on:close={() => {
s3FilePickerInner?.exit?.()
drawer?.closeDrawer?.()
}}
tooltip="Files present in the Workspace S3 bucket. You can set the workspace S3 bucket in the settings."
documentationLink="https://www.windmill.dev/docs/integrations/s3"
>
<S3FilePickerInner
bind:this={s3FilePickerInner}
on:selectAndClose={(e) => {
onSelectAndClose?.(e.detail)
drawer?.closeDrawer?.()
}}
{fromWorkspaceSettings}
{readOnlyMode}
{allowDelete}
bind:initialFileKey
bind:selectedFileKey
bind:workspaceSettingsInitialized
bind:storage
bind:allFilesByKey
bind:uploadModalOpen
{folderOnly}
{regexFilter}
{workspace}
/>
{#snippet actions()}
<div class="flex gap-1">
{#if secondaryStorageNames.current?.length}
<Select
inputClass="h-10 min-w-44 !placeholder-secondary"
items={[
{ value: undefined, label: 'Default storage' },
...secondaryStorageNames.current.map((value) => ({ value }))
]}
placeholder="Default storage"
bind:value={
() => storage,
(v) => {
if (v === storage) return
storage = v
s3FilePickerInner?.reloadContent?.()
}
}
/>
{/if}
{#if !readOnlyMode}
<Button
variant="border"
color="light"
disabled={workspaceSettingsInitialized === false}
startIcon={{ icon: FileUp }}
on:click={() => {
uploadModalOpen = true
}}>Upload File</Button
>
{#if !fromWorkspaceSettings && s3FilePickerInner}
<Button
disabled={selectedFileKey === undefined ||
emptyString(selectedFileKey.s3) ||
(folderOnly && allFilesByKey[selectedFileKey.s3]?.type !== 'folder')}
on:click={s3FilePickerInner.selectAndClose}>Select</Button
>
{/if}
{/if}
</div>
{/snippet}
</DrawerContent>
</Drawer>