Files
windmill/frontend/src/lib/components/sessions/openInSessionContext.ts
T
d3747d6255 feat(sessions): offer the item you came from when starting a new session (#10940)
* fix: connect to dev server instead of localhost

* fix: derive WebSocket scheme from location.protocol

Mirror the protocol-aware pattern used by initSqlWebSocket in dev.ts
so the WebSocket connects over wss:// when the dev server is reached
through an HTTPS proxy/tunnel, avoiding mixed-content blocking.

* refactor: drop now-unused port parameter of wmillTsDev

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsfdN82yP88qyQ3h8Lwv2v

* feat(sessions): offer the item you came from when starting a new session

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkrstcgtV4AC4jZRHVzFdm

* docs(sessions): state the new-session seed latch's real lifetime

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkrstcgtV4AC4jZRHVzFdm

* fix(sessions): let Enter act on the focused answer of the new-session offer

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkrstcgtV4AC4jZRHVzFdm

* feat(sessions): start on the item instead of resuming a stale session from the rail

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkrstcgtV4AC4jZRHVzFdm

* fix(sessions): hand the rail's item entry through the editor's own hand-off

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkrstcgtV4AC4jZRHVzFdm

* fix(sessions): snap the rail toggle back when a session switch does not navigate

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkrstcgtV4AC4jZRHVzFdm

---------

Co-authored-by: Nathan A. Ferch <nf+github@marginal.net>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 17:17:58 +02:00

58 lines
2.4 KiB
TypeScript

import { getContext, onDestroy, setContext } from 'svelte'
import type { OpenInSessionSource } from './OpenInSessionButton.svelte'
import type { PreviewItemRoute } from './previewPaths'
// The "Open in AI session" hand-off, published by the component that owns the
// item being edited (FlowBuilder, RawAppEditor) for AI entry points too deep in
// the tree to be handed it as a prop — the inline code editor's toolbar sits
// four levels below the builder, behind a recursive module wrapper.
const KEY = 'OpenInSessionHandoff'
export type OpenInSessionHandoff = {
/** The editor's hand-off, opening on `moduleId` when it addresses its parts
* (a flow step). `undefined` while the item has no path to open yet. */
source: (opts?: { moduleId?: string }) => OpenInSessionSource | undefined
}
// The hand-offs of every editor currently mounted. The navigation rail's "AI
// Sessions" switch sits above every page, out of reach of the context, and
// looks the editor of the item it is leaving up here instead.
const mounted = new Set<OpenInSessionHandoff>()
export function setOpenInSessionHandoff(handoff: OpenInSessionHandoff): void {
setContext(KEY, handoff)
onDestroy(registerMountedOpenInSessionHandoff(handoff))
}
/** Count `handoff` as mounted until the returned unregister runs. Split from
* setOpenInSessionHandoff so the registry can be driven outside a component. */
export function registerMountedOpenInSessionHandoff(handoff: OpenInSessionHandoff): () => void {
mounted.add(handoff)
return () => {
mounted.delete(handoff)
}
}
export function getOpenInSessionHandoff(): OpenInSessionHandoff | undefined {
return getContext<OpenInSessionHandoff | undefined>(KEY)
}
/** The mounted editor's hand-off for the item `route` names, or undefined when
* no editor on screen publishes one for it (a legacy app, a detail page).
* Matched on the item rather than taken as "the latest registered": a script
* editor mounted in a flow's drawer registers too, and the rail wants the
* page's own item. */
export function findMountedOpenInSessionSource(
route: PreviewItemRoute
): OpenInSessionSource | undefined {
const kind = route.kind === 'app' ? (route.raw_app ? 'raw_app' : undefined) : route.kind
if (!kind) return undefined
for (const handoff of mounted) {
const source = handoff.source()
const target = source?.target
if (target && target.kind === kind && target.path === route.itemPath) return source
}
return undefined
}