From 015ce0262e634a5278d0f7427f584ff447dc28a9 Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Thu, 23 Apr 2026 16:51:02 +0200 Subject: [PATCH] feat(dev): picker warns when wmill dev server is unreachable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Track WS state in Dev.svelte (connecting/open/closed) — 'closed' is set on either the WS error or close event. When closed, the picker replaces the toggle + search + tree with a warning Alert telling the user to run `wmill dev` from the workspace root. Toggle and search are hidden rather than rendered disabled because there's nothing to filter anyway. Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/src/lib/components/Dev.svelte | 96 ++++++++++++++++---------- 1 file changed, 58 insertions(+), 38 deletions(-) diff --git a/frontend/src/lib/components/Dev.svelte b/frontend/src/lib/components/Dev.svelte index beab7eaf1c..65b685353e 100644 --- a/frontend/src/lib/components/Dev.svelte +++ b/frontend/src/lib/components/Dev.svelte @@ -68,6 +68,7 @@ import SearchItems from '$lib/components/SearchItems.svelte' import TextInput from '$lib/components/text_input/TextInput.svelte' import Row from '$lib/components/common/table/Row.svelte' + import Alert from '$lib/components/common/alert/Alert.svelte' import { HOME_SEARCH_PLACEHOLDER } from '$lib/consts' import Toggle from './Toggle.svelte' import { setLicense } from '$lib/enterpriseUtils' @@ -209,6 +210,7 @@ let watchPath = $state(parseWatchPath()?.replace(PATH_SUFFIX_RE, '')) let pickerItems: WmPathItem[] = $state([]) const pickerMode = $derived(!watchPath) + let wsState: 'connecting' | 'open' | 'closed' = $state('connecting') let pickerFilter = $state('') let pickerKind: 'all' | 'flow' | 'script' | 'raw_app' = $state('all') // Shape pickerItems into the homepage's ItemType so we can reuse `groupItems` @@ -444,11 +446,13 @@ } const port = searchParams?.get('port') || '3001' try { + wsState = 'connecting' socket = new WebSocket(`ws://localhost:${port}/ws`) // On connect, request the watched path if any, otherwise ask for a list to render the picker socket.addEventListener('open', () => { if (!socket) return + wsState = 'open' if (watchPath) { socket.send(JSON.stringify({ type: 'loadWmPath', path: watchPath })) } else { @@ -456,6 +460,14 @@ } }) + socket.addEventListener('error', () => { + wsState = 'closed' + }) + + socket.addEventListener('close', () => { + wsState = 'closed' + }) + // Listen for messages socket.addEventListener('message', (event) => { replaceData(event.data) @@ -959,48 +971,56 @@ bind:filteredItems={pickerFilteredItems} /> -
- - {#snippet children({ item })} - - - - - {/snippet} - + {#if wsState !== 'closed'} +
+ + {#snippet children({ item })} + + + + + {/snippet} + -
- - - -
+ {/if} - {#if pickerItems.length === 0} + {#if wsState === 'closed'} + + Start it from your workspace root with + wmill dev + to preview your flows, scripts, and apps. + + {:else if pickerItems.length === 0}
No flows, scripts, or apps detected in this workspace.