{/snippet}
{#snippet actions()}
{#if !$userStore?.operator}
{#if showEditButton}
{#if script.use_codebase}
bundleThis script is deployed as a bundle and can only be deployed from the CLI for now
{:else if script.canWrite && !script.archived}
{/if}
{/if}
{#if !isCloudHosted() && editInForkAllowed($workspaceStore, $userWorkspaces) && (!showEditButton || !script.canWrite)}
{/if}
{/if}
0 ? script.summary : script.path}`}
aiDescription={`Open dropdown for script ${script.summary?.length > 0 ? script.summary : script.path} options`}
items={async () => {
let owner = isOwner(script.path, $userStore, $workspaceStore)
const canEdit = script.canWrite && showEditButton
if (script.draft_only) {
return [
...selectMenuItems(rowSelection),
{
displayName: 'View code',
icon: Code,
action: () => {
showCode(script.path, script.summary)
}
},
{
displayName: 'Move/Rename',
icon: FolderOpen,
action: () => {
// Addressed by the generated path its draft row sits at, but
// named by the path typed in the editor.
moveDrawer.openDrawer(script.draft_path ?? script.path, script.summary, 'script', {
storagePath: script.path
})
},
disabled: !showEditButton,
hide: $userStore?.operator
},
{
displayName: 'Delete',
icon: Trash,
action: (event) => {
// TODO
// @ts-ignore
if (event?.shiftKey) {
deleteScript(script.path)
} else {
deleteConfirmedCallback = () => {
deleteScript(script.path)
}
}
},
type: dlt,
// A draft-only row is always the authed user's own draft (the
// list endpoint only surfaces own/legacy draft-only rows), so
// discarding it never requires write permission on the path.
disabled: !showEditButton
}
]
}
return [
...selectMenuItems(rowSelection),
{
displayName: 'View code',
icon: Code,
action: () => {
showCode(script.path, script.summary)
}
},
...(script.auto_kind === 'wac'
? [
{
displayName: 'View JSON/YAML',
icon: FileText,
action: async () => {
const fullScript = await ScriptService.getScriptByPath({
workspace: $workspaceStore!,
path: script.path
})
wacExportDrawer?.open(fullScript)
}
}
]
: []),
{
displayName: 'Duplicate/Fork',
icon: GitFork,
href: `/scripts/add?template=${script.path}`,
disabled: !showEditButton,
hide: $userStore?.operator
},
{
displayName: editInForkLabel($workspaceStore, $userWorkspaces),
icon: Pen,
// No `href`: the handler resolves the destination asynchronously, and a melt
// menu item's anchor navigates before a delegated onclick can preventDefault it.
action: (e) => onEditInForkClick(e, 'script', script.path),
hide:
$userStore?.operator ||
isCloudHosted() ||
!editInForkAllowed($workspaceStore, $userWorkspaces)
},
{
displayName: 'Move/Rename',
icon: FolderOpen,
action: () => {
moveDrawer.openDrawer(script.path, script.summary, 'script')
},
disabled: !owner || script.archived || !canEdit,
hide: $userStore?.operator
},
...(isDeployable('script', script.path, await getDeployUiSettings())
? [
{
displayName: 'Deploy to staging/prod',
icon: ChevronUpSquare,
action: () => {
deploymentDrawer.openDrawer(script.path, 'script')
},
disabled: script.archived,
hide: $userStore?.operator
}
]
: []),
{
displayName: 'View runs',
icon: List,
href: `/runs/${script.path}`
},
{
displayName: 'Versions',
icon: History,
action: () => {
versionsDrawerOpen = true
}
},
{
displayName: 'Audit logs',
icon: Eye,
href: `/audit_logs?resource=${script.path}`,
hide: $userStore?.operator
},
{
displayName: 'Schedule',
icon: Calendar,
action: () => {
scheduleEditor?.openNew(false, script.path)
},
disabled: script.archived,
hide: $userStore?.operator
},
{
displayName: 'Permissions',
icon: Shield,
action: () => {
shareModal.openDrawer && shareModal.openDrawer(script.path, 'script')
},
disabled: script.archived,
hide: $userStore?.operator
},
{
displayName: 'Copy path',
icon: Copy,
action: () => {
copyToClipboard(script.path)
}
},
{
displayName: 'Publish to Hub',
icon: Globe2,
action: async () => {
// The row only carries metadata, so the code has to be fetched first; the tab is
// claimed before that, since Safari won't open one after an await.
const tab = claimTab()
try {
const fullScript = await ScriptService.getScriptByPath({
workspace: $workspaceStore!,
path: script.path
})
const url = scriptToHubUrl(fullScript, $hubBaseUrlStore).toString()
if (tab) {
tab.show(url)
} else if (!window.open(url)) {
sendUserToast('Allow popups to publish this script to the Hub', true)
}
} catch (e: any) {
tab?.discard()
sendUserToast(`Could not load ${script.path}: ${e?.body ?? e?.message ?? e}`, true)
}
},
// Operators can't write scripts, so they have nothing to publish.
hide: $disableHubStore || $userStore?.operator
},
{
displayName: script.archived ? 'Unarchive' : 'Archive',
icon: Archive,
action: () => {
script.archived
? script.path && unarchiveScript(script.path)
: script.path && archiveScript(script.path)
},
type: 'delete',
disabled: !owner || !canEdit,
hide: $userStore?.operator
},
...($userStore?.is_admin || $userStore?.is_super_admin
? [
{
displayName: 'Delete',
icon: Trash,
action: (event) => {
if (event?.shiftKey) {
deleteScript(script.path)
} else {
deleteConfirmedCallback = () => {
deleteScript(script.path)
}
}
},
type: dlt,
disabled: !canEdit,
hide: $userStore?.operator
}
]
: [])
]
}}
on:open={() => {
menuOpen = true
}}
/>
{/snippet}
{#if script}
(versionsDrawerOpen = false)}>
{
if (script) {
goto(`/scripts/get/${e.detail.version}?workspace=${$workspaceStore}`)
}
versionsDrawerOpen = false
}}
/>
{/if}