|
8 | 8 | import type { Run, ApiError, OutputFile, RunNetwork, Workflow } from '$lib/types.js'; |
9 | 9 | import { Button } from '$lib/components/ui/button'; |
10 | 10 | import { Skeleton } from '$lib/components/ui/skeleton'; |
11 | | - import { Terminal, RotateCw, X, Trash2, Loader2, MoreVertical, Settings2, ChevronRight, ExternalLink, FolderArchive, GitBranch, Clock, Calendar, Server } from 'lucide-svelte'; |
| 11 | + import { Terminal, RotateCw, X, Trash2, Loader2, MoreVertical, Settings2, ChevronRight, ExternalLink, FolderArchive, GitBranch, Clock, Calendar, Server, Globe, LockKeyhole } from 'lucide-svelte'; |
| 12 | + import { authStore } from '$lib/stores/auth.svelte.js'; |
12 | 13 | import { breadcrumbStore } from '$lib/stores/breadcrumb.svelte.js'; |
13 | 14 | import OutputFilesTree from '../components/OutputFilesTree.svelte'; |
14 | 15 | import WorkflowSection from '../components/WorkflowSection.svelte'; |
|
27 | 28 | let rerunning = $state(false); |
28 | 29 | let cancelling = $state(false); |
29 | 30 | let removing = $state(false); |
| 31 | + let togglingVisibility = $state(false); |
30 | 32 | let configOpen = $state(false); |
31 | 33 | let logsOpen = $state(true); |
32 | 34 |
|
|
101 | 103 | } |
102 | 104 | }); |
103 | 105 |
|
104 | | - const actionBusy = $derived(cancelling || rerunning || removing); |
| 106 | + const actionBusy = $derived(cancelling || rerunning || removing || togglingVisibility); |
105 | 107 |
|
106 | 108 | const duration = $derived.by(() => { |
107 | 109 | if (!isTerminal) tick; // reference tick to force re-evaluation |
|
283 | 285 | goto('/runs'); |
284 | 286 | }); |
285 | 287 |
|
| 288 | + const canEditRun = $derived( |
| 289 | + run && authStore.user && ( |
| 290 | + authStore.user.permissions?.includes('runs:manage_all') || |
| 291 | + run.owner?.id === authStore.user.id |
| 292 | + ) |
| 293 | + ); |
| 294 | +
|
| 295 | + const handleToggleVisibility = () => runAction(v => togglingVisibility = v, async () => { |
| 296 | + const newVis = run!.visibility === 'public' ? 'private' : 'public'; |
| 297 | + await runs.updateVisibility(run!.id, newVis); |
| 298 | + run = await runs.get(runId); |
| 299 | + }); |
| 300 | +
|
286 | 301 | function scrollToBottom() { |
287 | 302 | requestAnimationFrame(() => { |
288 | 303 | if (logContainer) { |
|
356 | 371 | Rerun |
357 | 372 | </DropdownMenu.Item> |
358 | 373 | {/if} |
| 374 | + {#if canEditRun} |
| 375 | + <DropdownMenu.Item onclick={handleToggleVisibility} disabled={actionBusy}> |
| 376 | + {#if run?.visibility === 'public'} |
| 377 | + <LockKeyhole class="h-4 w-4 mr-2" /> |
| 378 | + Make private |
| 379 | + {:else} |
| 380 | + <Globe class="h-4 w-4 mr-2" /> |
| 381 | + Make public |
| 382 | + {/if} |
| 383 | + </DropdownMenu.Item> |
| 384 | + {/if} |
359 | 385 | <DropdownMenu.Separator /> |
360 | 386 | <DropdownMenu.Item onclick={handleRemove} disabled={actionBusy} class="text-destructive focus:text-destructive"> |
361 | 387 | <Trash2 class="h-4 w-4 mr-2" /> |
|
391 | 417 | <Server class="h-3.5 w-3.5" /> |
392 | 418 | <span>{run.backend.name}</span> |
393 | 419 | </div> |
| 420 | + {#if authStore.authEnabled} |
| 421 | + <div class="h-4 w-px bg-border"></div> |
| 422 | + <div class="flex items-center gap-1.5"> |
| 423 | + {#if run.visibility === 'public'} |
| 424 | + <Globe class="h-3.5 w-3.5" /> |
| 425 | + <span>Public</span> |
| 426 | + {:else} |
| 427 | + <LockKeyhole class="h-3.5 w-3.5" /> |
| 428 | + <span>Private</span> |
| 429 | + {/if} |
| 430 | + </div> |
| 431 | + {/if} |
394 | 432 | {#if run.networks.length > 0} |
395 | 433 | <div class="h-4 w-px bg-border"></div> |
396 | 434 | <div class="flex items-center gap-1.5"> |
|
0 commit comments