From b727f16b8031776886c04f82905fcc28ebdb16c5 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Tue, 10 Feb 2026 01:16:13 +0200 Subject: [PATCH] feat: upgrade codemirror to 6.39.13 and enhance host switching --- bun.lock | 11 ++++----- package.json | 5 +++- packages/desktop/src-tauri/src/main.rs | 24 +++++++++++++++++++ packages/ui/package.json | 4 ++-- .../desktop/DesktopHostSwitcher.tsx | 23 ++++++++++++++++-- .../ui/src/components/ui/CodeMirrorEditor.tsx | 17 +++++++++---- 6 files changed, 68 insertions(+), 16 deletions(-) diff --git a/bun.lock b/bun.lock index a1ae58c1..11f19fda 100644 --- a/bun.lock +++ b/bun.lock @@ -23,7 +23,7 @@ "@codemirror/lint": "^6.9.2", "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.5.4", - "@codemirror/view": "^6.39.11", + "@codemirror/view": "^6.39.13", "@fontsource/ibm-plex-mono": "^5.2.7", "@fontsource/ibm-plex-sans": "^5.1.1", "@heroui/scroll-shadow": "^2.3.18", @@ -128,7 +128,7 @@ "@codemirror/lint": "^6.9.2", "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.5.4", - "@codemirror/view": "^6.39.11", + "@codemirror/view": "^6.39.13", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", @@ -301,6 +301,9 @@ }, }, }, + "overrides": { + "@codemirror/view": "6.39.13", + }, "packages": { "7zip-bin": ["7zip-bin@5.2.0", "", {}, "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A=="], @@ -3230,14 +3233,10 @@ "@codemirror/highlight/@codemirror/state": ["@codemirror/state@0.19.9", "", { "dependencies": { "@codemirror/text": "^0.19.0" } }, "sha512-psOzDolKTZkx4CgUqhBQ8T8gBc0xN5z4gzed109aF6x7D7umpDRoimacI/O6d9UGuyl4eYuDCZmDFr2Rq7aGOw=="], - "@codemirror/highlight/@codemirror/view": ["@codemirror/view@0.19.48", "", { "dependencies": { "@codemirror/rangeset": "^0.19.5", "@codemirror/state": "^0.19.3", "@codemirror/text": "^0.19.0", "style-mod": "^4.0.0", "w3c-keyname": "^2.2.4" } }, "sha512-0eg7D2Nz4S8/caetCTz61rK0tkHI17V/d15Jy0kLOT8dTLGGNJUponDnW28h2B6bERmPlVHKh8MJIr5OCp1nGw=="], - "@codemirror/highlight/@lezer/common": ["@lezer/common@0.15.12", "", {}, "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig=="], "@codemirror/history/@codemirror/state": ["@codemirror/state@0.19.9", "", { "dependencies": { "@codemirror/text": "^0.19.0" } }, "sha512-psOzDolKTZkx4CgUqhBQ8T8gBc0xN5z4gzed109aF6x7D7umpDRoimacI/O6d9UGuyl4eYuDCZmDFr2Rq7aGOw=="], - "@codemirror/history/@codemirror/view": ["@codemirror/view@0.19.48", "", { "dependencies": { "@codemirror/rangeset": "^0.19.5", "@codemirror/state": "^0.19.3", "@codemirror/text": "^0.19.0", "style-mod": "^4.0.0", "w3c-keyname": "^2.2.4" } }, "sha512-0eg7D2Nz4S8/caetCTz61rK0tkHI17V/d15Jy0kLOT8dTLGGNJUponDnW28h2B6bERmPlVHKh8MJIr5OCp1nGw=="], - "@codemirror/rangeset/@codemirror/state": ["@codemirror/state@0.19.9", "", { "dependencies": { "@codemirror/text": "^0.19.0" } }, "sha512-psOzDolKTZkx4CgUqhBQ8T8gBc0xN5z4gzed109aF6x7D7umpDRoimacI/O6d9UGuyl4eYuDCZmDFr2Rq7aGOw=="], "@electron/asar/commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="], diff --git a/package.json b/package.json index 8008423b..929996aa 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@codemirror/lint": "^6.9.2", "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.5.4", - "@codemirror/view": "^6.39.11", + "@codemirror/view": "^6.39.13", "@fontsource/ibm-plex-mono": "^5.2.7", "@fontsource/ibm-plex-sans": "^5.1.1", "@heroui/scroll-shadow": "^2.3.18", @@ -120,6 +120,9 @@ "zod": "^4.3.6", "zustand": "^5.0.8" }, + "overrides": { + "@codemirror/view": "6.39.13" + }, "devDependencies": { "@eslint/js": "^9.33.0", "@tailwindcss/postcss": "^4.0.0", diff --git a/packages/desktop/src-tauri/src/main.rs b/packages/desktop/src-tauri/src/main.rs index 2ae1844d..9f9d3151 100644 --- a/packages/desktop/src-tauri/src/main.rs +++ b/packages/desktop/src-tauri/src/main.rs @@ -1121,6 +1121,7 @@ async fn desktop_host_probe(url: String) -> Result { .build() .map_err(|err| err.to_string())?; let started = std::time::Instant::now(); + match client.get(&health).send().await { Ok(resp) => { let status = resp.status(); @@ -2086,6 +2087,29 @@ fn main() { } } + if initial_url != local_ui_url { + match desktop_host_probe(initial_url.clone()).await { + Ok(probe) if probe.status != "unreachable" => {} + Ok(_) => { + log::warn!( + "[desktop] startup host unreachable ({}), falling back to local ({})", + initial_url, + local_ui_url + ); + initial_url = local_ui_url.clone(); + } + Err(err) => { + log::warn!( + "[desktop] startup host probe failed ({}): {}, falling back to local ({})", + initial_url, + err, + local_ui_url + ); + initial_url = local_ui_url.clone(); + } + } + } + if let Err(err) = create_main_window(&handle, &initial_url, &local_origin) { log::error!("[desktop] failed to create window: {err}"); } diff --git a/packages/ui/package.json b/packages/ui/package.json index eb6df540..9726d27f 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -31,7 +31,7 @@ "@codemirror/lint": "^6.9.2", "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.5.4", - "@codemirror/view": "^6.39.11", + "@codemirror/view": "^6.39.13", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", @@ -102,4 +102,4 @@ "typescript-eslint": "^8.39.1", "vite": "^7.1.2" } -} \ No newline at end of file +} diff --git a/packages/ui/src/components/desktop/DesktopHostSwitcher.tsx b/packages/ui/src/components/desktop/DesktopHostSwitcher.tsx index d7fd6f4c..ce34c2c7 100644 --- a/packages/ui/src/components/desktop/DesktopHostSwitcher.tsx +++ b/packages/ui/src/components/desktop/DesktopHostSwitcher.tsx @@ -31,6 +31,7 @@ import { RiDeleteBinLine, } from '@remixicon/react'; import { cn } from '@/lib/utils'; +import { toast } from '@/components/ui'; import { isTauriShell, isDesktopShell } from '@/lib/desktop'; import { desktopHostProbe, @@ -160,6 +161,7 @@ export function DesktopHostSwitcherDialog({ const [isLoading, setIsLoading] = React.useState(false); const [isProbing, setIsProbing] = React.useState(false); const [isSaving, setIsSaving] = React.useState(false); + const [switchingHostId, setSwitchingHostId] = React.useState(null); const [error, setError] = React.useState(''); const [editingId, setEditingId] = React.useState(null); @@ -262,9 +264,25 @@ export function DesktopHostSwitcherDialog({ void probeAll(allHosts); }, [open, allHosts, probeAll]); - const handleSwitch = React.useCallback((host: DesktopHost) => { + const handleSwitch = React.useCallback(async (host: DesktopHost) => { const origin = host.id === LOCAL_HOST_ID ? getLocalOrigin() : (normalizeHostUrl(host.url) || ''); if (!origin) return; + + if (host.id !== LOCAL_HOST_ID && isTauriShell()) { + setSwitchingHostId(host.id); + const probe = await desktopHostProbe(origin).catch((): HostProbeResult => ({ status: 'unreachable', latencyMs: 0 })); + setStatusById((prev) => ({ + ...prev, + [host.id]: { status: probe.status, latencyMs: probe.latencyMs }, + })); + + if (probe.status === 'unreachable') { + toast.error(`Instance "${host.label}" is unreachable`); + setSwitchingHostId(null); + return; + } + } + const target = toNavigationUrl(origin); onHostSwitched?.(); @@ -440,7 +458,8 @@ export function DesktopHostSwitcherDialog({ 'flex items-center gap-2 flex-1 min-w-0 text-left', isEditing && 'pointer-events-none opacity-70' )} - onClick={() => handleSwitch(host)} + onClick={() => void handleSwitch(host)} + disabled={switchingHostId === host.id} aria-label={`Switch to ${host.label}`} > diff --git a/packages/ui/src/components/ui/CodeMirrorEditor.tsx b/packages/ui/src/components/ui/CodeMirrorEditor.tsx index e875b337..9664166a 100644 --- a/packages/ui/src/components/ui/CodeMirrorEditor.tsx +++ b/packages/ui/src/components/ui/CodeMirrorEditor.tsx @@ -2,7 +2,7 @@ import React from 'react'; import type { Extension } from '@codemirror/state'; import { Compartment, EditorState, RangeSetBuilder, StateField } from '@codemirror/state'; -import { Decoration, type DecorationSet, EditorView, ViewPlugin, WidgetType, gutters, keymap, lineNumbers } from '@codemirror/view'; +import { Decoration, type DecorationSet, EditorView, type KeyBinding, ViewPlugin, WidgetType, gutters, keymap, lineNumbers } from '@codemirror/view'; import { defaultKeymap, indentWithTab, history, historyKeymap } from '@codemirror/commands'; import { indentUnit } from '@codemirror/language'; import { search, searchKeymap, openSearchPanel, closeSearchPanel } from '@codemirror/search'; @@ -39,6 +39,13 @@ const highlightLinesCompartment = new Compartment(); const blockWidgetsCompartment = new Compartment(); const searchCompartment = new Compartment(); +const toViewKeyBindings = (bindings: readonly unknown[]): readonly KeyBinding[] => { + return bindings as readonly KeyBinding[]; +}; + +const openSearchPanelCompat = openSearchPanel as unknown as (view: EditorView) => void; +const closeSearchPanelCompat = closeSearchPanel as unknown as (view: EditorView) => void; + // BlockWidget class definition moved inside helper or adapted to take map class BlockWidget extends WidgetType { constructor(readonly id: string, readonly containerMap: Map) { @@ -198,7 +205,7 @@ export function CodeMirrorEditor({ externalExtensionsCompartment.of(extensions ?? []), highlightLinesCompartment.of(createHighlightLinesExtension(highlightLines)), blockWidgetsCompartment.of(createBlockWidgetsExtension(blockWidgets, widgetContainersRef.current)), - searchCompartment.of(enableSearch ? [search({ top: true }), keymap.of(searchKeymap)] : []), + searchCompartment.of(enableSearch ? [search({ top: true }), keymap.of(toViewKeyBindings(searchKeymap))] : []), ], }); @@ -232,7 +239,7 @@ export function CodeMirrorEditor({ externalExtensionsCompartment.reconfigure(extensions ?? []), highlightLinesCompartment.reconfigure(createHighlightLinesExtension(highlightLines)), blockWidgetsCompartment.reconfigure(createBlockWidgetsExtension(blockWidgets, widgetContainersRef.current)), - searchCompartment.reconfigure(enableSearch ? [search({ top: true }), keymap.of(searchKeymap)] : []), + searchCompartment.reconfigure(enableSearch ? [search({ top: true }), keymap.of(toViewKeyBindings(searchKeymap))] : []), ], }); @@ -247,9 +254,9 @@ export function CodeMirrorEditor({ return; } if (searchOpen) { - openSearchPanel(view); + openSearchPanelCompat(view); } else { - closeSearchPanel(view); + closeSearchPanelCompat(view); } }, [searchOpen, enableSearch]);