Files
windmill/frontend/src/lib/components/wizards/AppPicker.svelte
T
GuilhemandClaude Opus 5 338c4e921d feat: operators get AI sessions (#11443)
* feat: operators get AI sessions

`prefersSessionHandoff` stops taking the role, so every AI entry point an operator
can reach — Ask AI, the home composer, Open in session, the flow editor's hand-off,
the raw-app template picker — opens a session for them like anyone else. The
sessions page drops its refusal, and the layout stops keeping them on the docked
chat.

In the sidebar they now get the Workspace/AI Sessions switch and session mode, and
lose the Ask AI entry that stood in for them, since the pane behind it is no longer
the one they are sent to.

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

* feat: operators get run-oriented examples in the home composer

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

* docs: note builder_flows in the operator composer examples comment

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

* feat: gate session tools and editors on operator rights

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

* feat: keep operator AI sessions behind a browser opt-in

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

* fix: keep flows refused in the prompt for operators without builder rights

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

* refactor: decide every frontend edit check through one editRights module

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

* fix: let admins who are also operators open session editors, as drafts.rs does

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

* fix: mirror drafts.rs exactly in roleCanDraft and correct the edit-rights docs

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-09 18:22:34 +02:00

69 lines
1.9 KiB
Svelte

<script lang="ts">
import { getContext, onMount } from 'svelte'
import DarkModeObserver from '../DarkModeObserver.svelte'
import { AppService, type ListableApp } from '$lib/gen'
import { useEditRights } from '$lib/operatorWriteRights'
import type { AppViewerContext } from '../apps/types'
import Alert from '../common/alert/Alert.svelte'
import Select from '../select/Select.svelte'
import { useOperatingWorkspace } from '$lib/components/operatingWorkspace.svelte'
const operatingWorkspace = useOperatingWorkspace()
const editRights = useEditRights()
interface Props {
value?: string
}
let { value = $bindable('') }: Props = $props()
let darkMode = $state(false)
const { appPath } = getContext<AppViewerContext>('AppViewerContext')
let apps: ListableApp[] = $state([])
async function loadApps(): Promise<void> {
apps = (
await AppService.listApps({ workspace: $operatingWorkspace!, includeDraftOnly: true })
).map((app: ListableApp) => {
return {
canWrite:
editRights.canEditItem('app', app.path!, app.extra_perms) &&
app.workspace_id == $operatingWorkspace,
...app
}
})
}
onMount(() => {
loadApps()
if (value === '') value = $appPath
})
</script>
<DarkModeObserver bind:darkMode />
<div class="flex flex-col gap-2 w-full">
<Select
clearable
onClear={() => (value = '')}
bind:value
items={apps.map((app) => ({
value: app.path,
label: app.path === $appPath ? `${app.path} (current app)` : app.path
}))}
placeholder="Pick an app"
disablePortal
/>
{#if !appPath}
<Alert title="Current app not selectable" size="xs" type="warning" collapsible>
Current app is not selectable until you have deployed this app at least once.
</Alert>
{/if}
{#if appPath && $appPath === value}
<div class="text-2xs">
The current app is selected. If the path changes, the path needs to be updated manually.
</div>
{/if}
</div>