mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-04 00:02:17 +00:00
* feat: edit variables, resources and triggers in their own session tab Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: take a trigger's flow kind from its config and name the written trigger Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: one operating-workspace context for editors acting on a session's workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: reload page item tabs only in sessions acting on the tool's workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: resolve open in workspace for every preview tab kind in one place Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: guard every component under a session editor against the navigation workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: show a page item tab whose item is gone instead of mounting its editor Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: follow a saved page item in the tab's own workspace and restore in-frame rows Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: close a page item tab from its editor header Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: judge trigger permissions by the user acting in the session workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: judge every session-editor permission by the user acting in that workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep session permissions reactive to the acting role and restore tabs as last seen Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: default Path's owner controls to the user acting in its workspace Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
133 lines
4.5 KiB
Svelte
133 lines
4.5 KiB
Svelte
<script lang="ts">
|
|
import type { Snippet } from 'svelte'
|
|
import { ExternalLink, PanelRight } from 'lucide-svelte'
|
|
import { Button } from '$lib/components/common'
|
|
import RowIcon from '$lib/components/common/table/RowIcon.svelte'
|
|
import { newTabModifier } from '$lib/attachments/newTabModifier.svelte'
|
|
import {
|
|
hasToolDisplayActionHandler,
|
|
runToolDisplayAction
|
|
} from './createdResourceActions.svelte'
|
|
import {
|
|
workspaceItemAction,
|
|
type WindmillItemKind,
|
|
type WorkspaceItemTargetKind
|
|
} from './workspaceItems.svelte'
|
|
import { safeHref } from './safeHref'
|
|
|
|
type Props = {
|
|
href?: string
|
|
children?: Snippet
|
|
'data-wm-kind'?: WindmillItemKind
|
|
'data-wm-path'?: string
|
|
'data-wm-target-kind'?: WorkspaceItemTargetKind
|
|
'data-wm-raw-app'?: string
|
|
title?: string
|
|
}
|
|
let {
|
|
href,
|
|
children,
|
|
'data-wm-kind': wmKind,
|
|
'data-wm-path': wmPath,
|
|
'data-wm-target-kind': wmTargetKind,
|
|
'data-wm-raw-app': wmRawApp,
|
|
title
|
|
}: Props = $props()
|
|
|
|
// The drawers ride with the docked chat and the session tabs with the sessions page, so a
|
|
// surface can render this pill with nothing able to open one.
|
|
const available = $derived.by(() => {
|
|
const action = workspaceItemAction(wmKind, wmPath, wmTargetKind, wmRawApp === 'true')
|
|
return action && hasToolDisplayActionHandler(action.type) ? action : undefined
|
|
})
|
|
// Only the preview panel takes the plain click. A drawer keeps its own button beside an
|
|
// outbound link: the docked chat mounts drawer handlers on nearly every page, so claiming
|
|
// that click would redirect these pills far outside the sessions page.
|
|
const previewAction = $derived(available?.type === 'open_item_preview' ? available : undefined)
|
|
const drawerAction = $derived(available?.type === 'open_created_resource' ? available : undefined)
|
|
|
|
const allowedHref = $derived(safeHref(href, window.location.href))
|
|
|
|
const modifier = newTabModifier()
|
|
|
|
const hint = $derived(
|
|
previewAction ? `Open ${wmPath} in the preview panel` : `Open ${wmPath} in a new tab`
|
|
)
|
|
|
|
async function openDrawer(event?: Event) {
|
|
event?.preventDefault()
|
|
event?.stopPropagation()
|
|
if (drawerAction) {
|
|
await runToolDisplayAction(drawerAction)
|
|
}
|
|
}
|
|
|
|
async function onclick(event: MouseEvent) {
|
|
// Modifier clicks are the only remaining route to the tab once the plain click is
|
|
// spoken for, so leave them to the browser.
|
|
if (!previewAction || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return
|
|
event.preventDefault()
|
|
await runToolDisplayAction(previewAction)
|
|
}
|
|
</script>
|
|
|
|
{#if allowedHref}
|
|
{#if wmKind}
|
|
<!-- Only a preview pill can change icon, so only it is worth tracking the modifier for. -->
|
|
<span
|
|
class="group inline-flex items-baseline"
|
|
{@attach previewAction ? modifier.attach : undefined}
|
|
>
|
|
<a
|
|
href={allowedHref}
|
|
target={previewAction ? undefined : '_blank'}
|
|
rel={previewAction ? undefined : 'noopener noreferrer'}
|
|
title={title || hint}
|
|
{onclick}
|
|
class="inline-flex items-baseline gap-1 px-1 rounded hover:bg-surface-hover text-primary no-underline font-mono text-[0.9em] align-baseline"
|
|
>
|
|
<!-- Kind icon and action icon share one fixed 12px box, so the pill is the same
|
|
width at rest and on hover and the surrounding sentence never reflows. -->
|
|
<span class="relative inline-flex self-center shrink-0 w-3 h-3">
|
|
<span class="absolute inset-0 transition-opacity group-hover:opacity-0">
|
|
<RowIcon kind={wmKind} size={12} />
|
|
</span>
|
|
<span
|
|
class="absolute inset-0 flex items-center justify-center text-tertiary opacity-0 transition-opacity group-hover:opacity-100"
|
|
>
|
|
<!-- Narrower than the modifier list `onclick` bails on: only the modifier that
|
|
really opens a tab may show the icon for one. -->
|
|
{#if previewAction && !modifier.held}
|
|
<PanelRight size={12} />
|
|
{:else}
|
|
<ExternalLink size={11} />
|
|
{/if}
|
|
</span>
|
|
</span>
|
|
{@render children?.()}
|
|
</a>
|
|
{#if drawerAction}
|
|
<Button
|
|
type="button"
|
|
size="xs3"
|
|
variant="subtle"
|
|
iconOnly
|
|
startIcon={{ icon: PanelRight }}
|
|
title="Open in editor"
|
|
aria-label="Open {wmPath} in editor"
|
|
wrapperClasses="ml-0.5 inline-flex self-center shrink-0 opacity-0 group-hover:opacity-100 transition-opacity"
|
|
btnClasses="!w-auto !rounded !p-0.5 !text-tertiary"
|
|
onClick={openDrawer}
|
|
/>
|
|
{/if}
|
|
</span>
|
|
{:else}
|
|
<a href={allowedHref} target="_blank" rel="noopener noreferrer" {title}>
|
|
{@render children?.()}
|
|
</a>
|
|
{/if}
|
|
{:else}
|
|
<!-- An empty or unsafe href still has text; drop only the link. -->
|
|
{@render children?.()}
|
|
{/if}
|