Files
windmill/frontend/src/lib/components/PasswordArgInput.svelte
T
Ruben FiszelandClaude Opus 4.6 26050f96c3 feat: support multiline secrets in resource password fields (#8637)
* feat: support multiline secrets in resource password fields

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

* fix: wire minRows through to password textarea instead of disabling it

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

* refactor: auto-detect multiline in password field instead of always using textarea

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

* fix: switch to textarea on Enter keypress in password field

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

* fix: focus textarea after switching from single-line password input

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

* refactor: simplify Password multiline logic and fix cursor position bug

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 09:33:26 +00:00

101 lines
2.3 KiB
Svelte

<script lang="ts">
import { VariableService } from '$lib/gen'
import { userStore, workspaceStore } from '$lib/stores'
import { generateRandomString } from '$lib/utils'
import { Button } from './common'
import Password from './Password.svelte'
import { untrack } from 'svelte'
interface Props {
value?: string | undefined
disabled: boolean
minRows?: number
}
let { value = $bindable(undefined), disabled, minRows }: Props = $props()
let path = $state('')
let password = $state(
value && typeof value === 'string' && !value.startsWith('$var:') ? value : ''
)
let isGenerating = false
let userPrefix = $derived(
'u/' + ($userStore?.username ?? $userStore?.email)?.split('@')[0] + '/secret_arg/'
)
async function generateValue() {
if (isGenerating) return
isGenerating = true
try {
let npath = userPrefix + generateRandomString(12)
let nvalue = '$var:' + npath
await VariableService.createVariable({
workspace: $workspaceStore!,
requestBody: {
value: password,
is_secret: true,
path: npath,
description: 'Ephemeral secret variable',
expires_at: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString()
}
})
path = npath
console.log('generated', nvalue)
value = nvalue
debouncedUpdate()
} finally {
isGenerating = false
}
}
async function updateValue() {
try {
await VariableService.updateVariable({
workspace: $workspaceStore!,
path: path,
requestBody: {
value: password
}
})
} catch (e) {
generateValue()
}
}
let timeout: number | undefined = undefined
function debouncedUpdate() {
timeout && clearTimeout(timeout)
timeout = setTimeout(updateValue, 500)
}
$effect(() => {
password && untrack(() => debouncedUpdate())
})
$effect(() => {
$workspaceStore &&
($userStore?.username || $userStore?.email) &&
path == '' &&
password != '' &&
untrack(() => generateValue())
})
</script>
{#if value?.startsWith('$var:') && !value.startsWith('$var:' + userPrefix)}
<div class="flex items-center gap-2 text-sm text-primary">
Linked to static variable
<Button
size="xs"
variant="default"
onclick={() => {
value = ''
}}
>
Reset variable link
</Button>
</div>
{:else}
<Password {disabled} {minRows} bind:password />
{/if}