pub use super::generated_api::api::{
    action::{Action as ProtobufAction, PaneIdAndShouldFloat, SwitchToModePayload},
    event::{
        EventNameList as ProtobufEventNameList, Header,
        ResurrectableSession as ProtobufResurrectableSession,
        SessionManifest as ProtobufSessionManifest,
    },
    input_mode::InputMode as ProtobufInputMode,
    pane_frame_style::PaneFrameStyle as ProtobufPaneFrameStyle,
    plugin_command::{
        break_panes_to_new_tab_response, break_panes_to_tab_with_id_response,
        break_panes_to_tab_with_index_response, delete_layout_response, dump_layout_response,
        dump_session_layout_response, edit_layout_response, focus_or_create_tab_response,
        get_focused_pane_info_response, get_pane_cwd_response, get_pane_pid_response,
        get_pane_running_command_response, get_session_list_response, hide_floating_panes_response,
        highlight_style::Style as ProtobufHighlightStyleVariant, new_tab_response,
        new_tab_unfocused_response, parse_layout_response, plugin_command::Payload,
        rename_layout_response, save_layout_response, save_session_response,
        show_floating_panes_response, BreakPanesToNewTabPayload,
        BreakPanesToNewTabResponse as ProtobufBreakPanesToNewTabResponse,
        BreakPanesToTabWithIdPayload,
        BreakPanesToTabWithIdResponse as ProtobufBreakPanesToTabWithIdResponse,
        BreakPanesToTabWithIndexPayload,
        BreakPanesToTabWithIndexResponse as ProtobufBreakPanesToTabWithIndexResponse,
        ChangeFloatingPanesCoordinatesPayload, ChangeHostFolderPayload, ClearPaneHighlightsPayload,
        ClearScreenForPaneIdPayload, CliPipeOutputPayload, CloseMultiplePanesPayload,
        CloseTabWithIdPayload, CloseTabWithIndexPayload, CommandName, ContextItem,
        CopyToClipboardPayload, CreateTokenResponse as ProtobufCreateTokenResponse,
        CreateTokenResponse, CurrentSessionLastSavedTimePayload,
        CurrentSessionLastSavedTimeResponse as ProtobufCurrentSessionLastSavedTimeResponse,
        CursorPosition, CustomIndexHighlight as ProtobufCustomIndexHighlight,
        CustomRgbHighlight as ProtobufCustomRgbHighlight,
        DeleteAllDeadSessionsResponse as ProtobufDeleteAllDeadSessionsResponse,
        DeleteDeadSessionResponse as ProtobufDeleteDeadSessionResponse, DeleteLayoutPayload,
        DeleteLayoutResponse as ProtobufDeleteLayoutResponse, DumpLayoutPayload,
        DumpLayoutResponse as ProtobufDumpLayoutResponse, DumpSessionLayoutPayload,
        DumpSessionLayoutResponse as ProtobufDumpSessionLayoutResponse, EditLayoutPayload,
        EditLayoutResponse as ProtobufEditLayoutResponse, EditScrollbackForPaneWithIdPayload,
        EmbedMultiplePanesPayload, EnvVariable, ExecCmdPayload,
        FixedOrPercent as ProtobufFixedOrPercent,
        FixedOrPercentValue as ProtobufFixedOrPercentValue, FloatMultiplePanesPayload,
        FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates,
        FocusOrCreateTabResponse as ProtobufFocusOrCreateTabResponse, FocusedPaneInfo,
        GenerateRandomNamePayload,
        GenerateRandomNameResponse as ProtobufGenerateRandomNameResponse,
        GenerateWebLoginTokenPayload, GetFocusedPaneInfoPayload,
        GetFocusedPaneInfoResponse as ProtobufGetFocusedPaneInfoResponse, GetLayoutDirPayload,
        GetLayoutDirResponse as ProtobufGetLayoutDirResponse,
        GetPaneCwdPayload as ProtobufGetPaneCwdPayload,
        GetPaneCwdResponse as ProtobufGetPaneCwdResponse, GetPaneInfoPayload,
        GetPaneInfoResponse as ProtobufGetPaneInfoResponse, GetPanePidPayload,
        GetPanePidResponse as ProtobufGetPanePidResponse,
        GetPaneRunningCommandPayload as ProtobufGetPaneRunningCommandPayload,
        GetPaneRunningCommandResponse as ProtobufGetPaneRunningCommandResponse,
        GetPaneScrollbackPayload,
        GetSessionEnvironmentVariablesPayload as ProtobufGetSessionEnvironmentVariablesPayload,
        GetSessionEnvironmentVariablesResponse as ProtobufGetSessionEnvironmentVariablesResponse,
        GetSessionListPayload as ProtobufGetSessionListPayload,
        GetSessionListResponse as ProtobufGetSessionListResponse, GetTabInfoPayload,
        GetTabInfoResponse as ProtobufGetTabInfoResponse, GoToTabWithIdPayload,
        GroupAndUngroupPanesPayload, HideFloatingPanesPayload as ProtobufHideFloatingPanesPayload,
        HideFloatingPanesResponse as ProtobufHideFloatingPanesResponse, HidePaneWithIdPayload,
        HighlightAndUnhighlightPanesPayload, HighlightLayer as ProtobufHighlightLayer,
        HighlightStyle as ProtobufHighlightStyle, HttpVerb as ProtobufHttpVerb, IdAndNewName,
        KeyToRebind, KeyToUnbind, KillSessionsPayload,
        KillSessionsResponse as ProtobufKillSessionsResponse, ListTokensResponse,
        LoadNewPluginPayload, MessageToPluginPayload, MovePaneWithPaneIdInDirectionPayload,
        MovePaneWithPaneIdPayload, MovePayload, NewPluginArgs as ProtobufNewPluginArgs,
        NewTabPayload, NewTabResponse as ProtobufNewTabResponse, NewTabUnfocusedPayload,
        NewTabUnfocusedResponse as ProtobufNewTabUnfocusedResponse,
        NewTabsResponse as ProtobufNewTabsResponse, NewTabsWithLayoutInfoPayload,
        NewTiledPaneInTabPayload, NewTiledPaneInTabResponse as ProtobufNewTiledPaneInTabResponse,
        OpenCommandPaneBackgroundResponse as ProtobufOpenCommandPaneBackgroundResponse,
        OpenCommandPaneFloatingNearPluginPayload,
        OpenCommandPaneFloatingNearPluginResponse as ProtobufOpenCommandPaneFloatingNearPluginResponse,
        OpenCommandPaneFloatingResponse as ProtobufOpenCommandPaneFloatingResponse,
        OpenCommandPaneInPlaceOfPaneIdPayload,
        OpenCommandPaneInPlaceOfPaneIdResponse as ProtobufOpenCommandPaneInPlaceOfPaneIdResponse,
        OpenCommandPaneInPlaceOfPluginPayload,
        OpenCommandPaneInPlaceOfPluginResponse as ProtobufOpenCommandPaneInPlaceOfPluginResponse,
        OpenCommandPaneInPlaceResponse as ProtobufOpenCommandPaneInPlaceResponse,
        OpenCommandPaneNearPluginPayload,
        OpenCommandPaneNearPluginResponse as ProtobufOpenCommandPaneNearPluginResponse,
        OpenCommandPanePayload, OpenCommandPaneResponse as ProtobufOpenCommandPaneResponse,
        OpenEditPaneInPlaceOfPaneIdPayload,
        OpenEditPaneInPlaceOfPaneIdResponse as ProtobufOpenEditPaneInPlaceOfPaneIdResponse,
        OpenFileFloatingNearPluginPayload,
        OpenFileFloatingNearPluginResponse as ProtobufOpenFileFloatingNearPluginResponse,
        OpenFileFloatingResponse as ProtobufOpenFileFloatingResponse,
        OpenFileInPlaceOfPluginPayload,
        OpenFileInPlaceOfPluginResponse as ProtobufOpenFileInPlaceOfPluginResponse,
        OpenFileInPlaceResponse as ProtobufOpenFileInPlaceResponse, OpenFileNearPluginPayload,
        OpenFileNearPluginResponse as ProtobufOpenFileNearPluginResponse, OpenFilePayload,
        OpenFileResponse as ProtobufOpenFileResponse,
        OpenPaneInNewTabResponse as ProtobufOpenPaneInNewTabResponse,
        OpenPluginPaneFloatingPayload,
        OpenPluginPaneFloatingResponse as ProtobufOpenPluginPaneFloatingResponse,
        OpenPluginPaneInNewTabPayload as ProtobufOpenPluginPaneInNewTabPayload,
        OpenTerminalFloatingNearPluginPayload,
        OpenTerminalFloatingNearPluginResponse as ProtobufOpenTerminalFloatingNearPluginResponse,
        OpenTerminalFloatingResponse as ProtobufOpenTerminalFloatingResponse,
        OpenTerminalInPlaceOfPluginPayload,
        OpenTerminalInPlaceOfPluginResponse as ProtobufOpenTerminalInPlaceOfPluginResponse,
        OpenTerminalInPlaceResponse as ProtobufOpenTerminalInPlaceResponse,
        OpenTerminalNearPluginPayload,
        OpenTerminalNearPluginResponse as ProtobufOpenTerminalNearPluginResponse,
        OpenTerminalPaneInPlaceOfPaneIdPayload,
        OpenTerminalPaneInPlaceOfPaneIdResponse as ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse,
        OpenTerminalResponse as ProtobufOpenTerminalResponse, OverrideLayoutPayload,
        PageScrollDownInPaneIdPayload, PageScrollUpInPaneIdPayload, PaneId as ProtobufPaneId,
        PaneIdAndFloatingPaneCoordinates, PaneType as ProtobufPaneType, ParseLayoutPayload,
        ParseLayoutResponse as ProtobufParseLayoutResponse, PluginCommand as ProtobufPluginCommand,
        PluginMessagePayload, RebindKeysPayload, ReconfigurePayload,
        RegexHighlight as ProtobufRegexHighlight, ReloadPluginPayload, RenameLayoutPayload,
        RenameLayoutResponse as ProtobufRenameLayoutResponse, RenameTabWithIdPayload,
        RenameWebLoginTokenPayload, RenameWebTokenResponse, ReplacePaneWithExistingPanePayload,
        RequestPluginPermissionPayload, RerunCommandPanePayload, ResizePaneIdWithDirectionPayload,
        ResizePayload, RevokeAllWebTokensResponse, RevokeTokenResponse, RevokeWebLoginTokenPayload,
        RunActionPayload, RunCommandPayload, RunningCommand as ProtobufRunningCommand,
        SaveLayoutPayload, SaveLayoutResponse as ProtobufSaveLayoutResponse, SaveSessionPayload,
        SaveSessionResponse as ProtobufSaveSessionResponse, ScrollDownInPaneIdPayload,
        ScrollToBottomInPaneIdPayload, ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload,
        SessionListSnapshot as ProtobufSessionListSnapshot, SetFloatingPanePinnedPayload,
        SetPaneBorderlessPayload, SetPaneColorPayload,
        SetPaneFrameStylePayload as ProtobufSetPaneFrameStylePayload,
        SetPaneRegexHighlightsPayload, SetSelfMouseSelectionSupportPayload,
        SetSoftKeyboardPayload as ProtobufSetSoftKeyboardPayload, SetTimeoutPayload,
        ShowCursorPayload, ShowFloatingPanesPayload as ProtobufShowFloatingPanesPayload,
        ShowFloatingPanesResponse as ProtobufShowFloatingPanesResponse, ShowPaneWithIdPayload,
        StackPanesPayload, SubscribePayload, SwitchSessionPayload, SwitchTabToIdPayload,
        SwitchTabToPayload, ToggleFloatingPanesPayload, TogglePaneBorderlessPayload,
        TogglePaneEmbedOrEjectForPaneIdPayload, TogglePaneIdFullscreenPayload, UnsubscribePayload,
        WebRequestPayload, WriteCharsToPaneIdPayload, WriteToPaneIdPayload,
    },
    plugin_permission::PermissionType as ProtobufPermissionType,
    resize::ResizeAction as ProtobufResizeAction,
};

use crate::data::{
    ConnectToSession, DeleteAllDeadSessionsResponse, DeleteDeadSessionResponse,
    DeleteLayoutResponse, EditLayoutResponse, FloatingPaneCoordinates, GetFocusedPaneInfoResponse,
    GetPaneCwdResponse, GetPanePidResponse, GetPaneRunningCommandResponse, GetSessionListResponse,
    HighlightLayer, HighlightStyle, HttpVerb, InputMode, KeyWithModifier, KillSessionsResponse,
    MessageToPlugin, NewPluginArgs, PaneId, PermissionType, PluginCommand, RegexHighlight,
    RenameLayoutResponse, SaveLayoutResponse, SessionInfo, SessionListSnapshot,
};
use crate::input::actions::Action;
use crate::input::layout::PercentOrFixed;

use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::path::PathBuf;

impl Into<FloatingPaneCoordinates> for ProtobufFloatingPaneCoordinates {
    fn into(self) -> FloatingPaneCoordinates {
        FloatingPaneCoordinates {
            x: self
                .x
                .and_then(|x| match ProtobufFixedOrPercent::try_from(x.r#type).ok() {
                    Some(ProtobufFixedOrPercent::Percent) => {
                        Some(PercentOrFixed::Percent(x.value as usize))
                    },
                    Some(ProtobufFixedOrPercent::Fixed) => {
                        Some(PercentOrFixed::Fixed(x.value as usize))
                    },
                    None => None,
                }),
            y: self
                .y
                .and_then(|y| match ProtobufFixedOrPercent::try_from(y.r#type).ok() {
                    Some(ProtobufFixedOrPercent::Percent) => {
                        Some(PercentOrFixed::Percent(y.value as usize))
                    },
                    Some(ProtobufFixedOrPercent::Fixed) => {
                        Some(PercentOrFixed::Fixed(y.value as usize))
                    },
                    None => None,
                }),
            width: self.width.and_then(|width| {
                match ProtobufFixedOrPercent::try_from(width.r#type).ok() {
                    Some(ProtobufFixedOrPercent::Percent) => {
                        Some(PercentOrFixed::Percent(width.value as usize))
                    },
                    Some(ProtobufFixedOrPercent::Fixed) => {
                        Some(PercentOrFixed::Fixed(width.value as usize))
                    },
                    None => None,
                }
            }),
            height: self.height.and_then(|height| {
                match ProtobufFixedOrPercent::try_from(height.r#type).ok() {
                    Some(ProtobufFixedOrPercent::Percent) => {
                        Some(PercentOrFixed::Percent(height.value as usize))
                    },
                    Some(ProtobufFixedOrPercent::Fixed) => {
                        Some(PercentOrFixed::Fixed(height.value as usize))
                    },
                    None => None,
                }
            }),
            pinned: self.pinned,
            borderless: self.borderless,
        }
    }
}

impl Into<ProtobufFloatingPaneCoordinates> for FloatingPaneCoordinates {
    fn into(self) -> ProtobufFloatingPaneCoordinates {
        ProtobufFloatingPaneCoordinates {
            x: match self.x {
                Some(PercentOrFixed::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
                    r#type: ProtobufFixedOrPercent::Percent as i32,
                    value: percent as u32,
                }),
                Some(PercentOrFixed::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
                    r#type: ProtobufFixedOrPercent::Fixed as i32,
                    value: fixed as u32,
                }),
                None => None,
            },
            y: match self.y {
                Some(PercentOrFixed::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
                    r#type: ProtobufFixedOrPercent::Percent as i32,
                    value: percent as u32,
                }),
                Some(PercentOrFixed::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
                    r#type: ProtobufFixedOrPercent::Fixed as i32,
                    value: fixed as u32,
                }),
                None => None,
            },
            width: match self.width {
                Some(PercentOrFixed::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
                    r#type: ProtobufFixedOrPercent::Percent as i32,
                    value: percent as u32,
                }),
                Some(PercentOrFixed::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
                    r#type: ProtobufFixedOrPercent::Fixed as i32,
                    value: fixed as u32,
                }),
                None => None,
            },
            height: match self.height {
                Some(PercentOrFixed::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
                    r#type: ProtobufFixedOrPercent::Percent as i32,
                    value: percent as u32,
                }),
                Some(PercentOrFixed::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
                    r#type: ProtobufFixedOrPercent::Fixed as i32,
                    value: fixed as u32,
                }),
                None => None,
            },
            pinned: self.pinned,
            borderless: self.borderless,
        }
    }
}

impl Into<HttpVerb> for ProtobufHttpVerb {
    fn into(self) -> HttpVerb {
        match self {
            ProtobufHttpVerb::Get => HttpVerb::Get,
            ProtobufHttpVerb::Post => HttpVerb::Post,
            ProtobufHttpVerb::Put => HttpVerb::Put,
            ProtobufHttpVerb::Delete => HttpVerb::Delete,
        }
    }
}

impl Into<ProtobufHttpVerb> for HttpVerb {
    fn into(self) -> ProtobufHttpVerb {
        match self {
            HttpVerb::Get => ProtobufHttpVerb::Get,
            HttpVerb::Post => ProtobufHttpVerb::Post,
            HttpVerb::Put => ProtobufHttpVerb::Put,
            HttpVerb::Delete => ProtobufHttpVerb::Delete,
        }
    }
}

impl TryFrom<ProtobufPaneId> for PaneId {
    type Error = &'static str;
    fn try_from(protobuf_pane_id: ProtobufPaneId) -> Result<Self, &'static str> {
        match ProtobufPaneType::try_from(protobuf_pane_id.pane_type).ok() {
            Some(ProtobufPaneType::Terminal) => Ok(PaneId::Terminal(protobuf_pane_id.id)),
            Some(ProtobufPaneType::Plugin) => Ok(PaneId::Plugin(protobuf_pane_id.id)),
            None => Err("Failed to convert PaneId"),
        }
    }
}

impl TryFrom<PaneId> for ProtobufPaneId {
    type Error = &'static str;
    fn try_from(pane_id: PaneId) -> Result<Self, &'static str> {
        match pane_id {
            PaneId::Terminal(id) => Ok(ProtobufPaneId {
                pane_type: ProtobufPaneType::Terminal as i32,
                id,
            }),
            PaneId::Plugin(id) => Ok(ProtobufPaneId {
                pane_type: ProtobufPaneType::Plugin as i32,
                id,
            }),
        }
    }
}

impl TryFrom<ProtobufGetPanePidResponse> for GetPanePidResponse {
    type Error = &'static str;
    fn try_from(protobuf_response: ProtobufGetPanePidResponse) -> Result<Self, &'static str> {
        match protobuf_response.result {
            Some(get_pane_pid_response::Result::Pid(pid)) => Ok(GetPanePidResponse::Ok(pid)),
            Some(get_pane_pid_response::Result::Error(error)) => Ok(GetPanePidResponse::Err(error)),
            None => Err("Empty GetPanePidResponse"),
        }
    }
}

impl From<GetPanePidResponse> for ProtobufGetPanePidResponse {
    fn from(response: GetPanePidResponse) -> Self {
        match response {
            GetPanePidResponse::Ok(pid) => ProtobufGetPanePidResponse {
                result: Some(get_pane_pid_response::Result::Pid(pid)),
            },
            GetPanePidResponse::Err(error) => ProtobufGetPanePidResponse {
                result: Some(get_pane_pid_response::Result::Error(error)),
            },
        }
    }
}

impl From<GetPaneRunningCommandResponse> for ProtobufGetPaneRunningCommandResponse {
    fn from(response: GetPaneRunningCommandResponse) -> Self {
        match response {
            GetPaneRunningCommandResponse::Ok(args) => ProtobufGetPaneRunningCommandResponse {
                result: Some(get_pane_running_command_response::Result::Command(
                    ProtobufRunningCommand { args },
                )),
            },
            GetPaneRunningCommandResponse::Err(err) => ProtobufGetPaneRunningCommandResponse {
                result: Some(get_pane_running_command_response::Result::Error(err)),
            },
        }
    }
}

impl From<GetSessionListResponse> for ProtobufGetSessionListResponse {
    fn from(response: GetSessionListResponse) -> Self {
        match response {
            GetSessionListResponse::Ok(snapshot) => {
                let live_sessions = snapshot
                    .live_sessions
                    .into_iter()
                    .filter_map(|si| ProtobufSessionManifest::try_from(si).ok())
                    .collect();
                let resurrectable_sessions = snapshot
                    .resurrectable_sessions
                    .into_iter()
                    .map(|entry| ProtobufResurrectableSession::from(entry))
                    .collect();
                ProtobufGetSessionListResponse {
                    result: Some(get_session_list_response::Result::Snapshot(
                        ProtobufSessionListSnapshot {
                            live_sessions,
                            resurrectable_sessions,
                        },
                    )),
                }
            },
            GetSessionListResponse::Err(err) => ProtobufGetSessionListResponse {
                result: Some(get_session_list_response::Result::Error(err)),
            },
        }
    }
}

impl TryFrom<ProtobufGetSessionListResponse> for GetSessionListResponse {
    type Error = &'static str;
    fn try_from(protobuf_response: ProtobufGetSessionListResponse) -> Result<Self, &'static str> {
        match protobuf_response.result {
            Some(get_session_list_response::Result::Snapshot(snapshot)) => {
                let mut live_sessions: Vec<SessionInfo> = Vec::new();
                for manifest in snapshot.live_sessions {
                    live_sessions.push(SessionInfo::try_from(manifest)?);
                }
                let resurrectable_sessions = snapshot
                    .resurrectable_sessions
                    .into_iter()
                    .map(<(String, std::time::Duration)>::from)
                    .collect();
                Ok(GetSessionListResponse::Ok(SessionListSnapshot {
                    live_sessions,
                    resurrectable_sessions,
                }))
            },
            Some(get_session_list_response::Result::Error(err)) => {
                Ok(GetSessionListResponse::Err(err))
            },
            None => Err("Empty GetSessionListResponse"),
        }
    }
}

impl From<KillSessionsResponse> for ProtobufKillSessionsResponse {
    fn from(response: KillSessionsResponse) -> Self {
        match response {
            KillSessionsResponse::Ok => ProtobufKillSessionsResponse { error: None },
            KillSessionsResponse::Err(err) => ProtobufKillSessionsResponse { error: Some(err) },
        }
    }
}

impl From<ProtobufKillSessionsResponse> for KillSessionsResponse {
    fn from(protobuf_response: ProtobufKillSessionsResponse) -> Self {
        match protobuf_response.error {
            Some(err) => KillSessionsResponse::Err(err),
            None => KillSessionsResponse::Ok,
        }
    }
}

impl From<DeleteDeadSessionResponse> for ProtobufDeleteDeadSessionResponse {
    fn from(response: DeleteDeadSessionResponse) -> Self {
        match response {
            DeleteDeadSessionResponse::Ok => ProtobufDeleteDeadSessionResponse { error: None },
            DeleteDeadSessionResponse::Err(err) => {
                ProtobufDeleteDeadSessionResponse { error: Some(err) }
            },
        }
    }
}

impl From<ProtobufDeleteDeadSessionResponse> for DeleteDeadSessionResponse {
    fn from(protobuf_response: ProtobufDeleteDeadSessionResponse) -> Self {
        match protobuf_response.error {
            Some(err) => DeleteDeadSessionResponse::Err(err),
            None => DeleteDeadSessionResponse::Ok,
        }
    }
}

impl From<DeleteAllDeadSessionsResponse> for ProtobufDeleteAllDeadSessionsResponse {
    fn from(response: DeleteAllDeadSessionsResponse) -> Self {
        match response {
            DeleteAllDeadSessionsResponse::Ok => {
                ProtobufDeleteAllDeadSessionsResponse { error: None }
            },
            DeleteAllDeadSessionsResponse::Err(err) => {
                ProtobufDeleteAllDeadSessionsResponse { error: Some(err) }
            },
        }
    }
}

impl From<ProtobufDeleteAllDeadSessionsResponse> for DeleteAllDeadSessionsResponse {
    fn from(protobuf_response: ProtobufDeleteAllDeadSessionsResponse) -> Self {
        match protobuf_response.error {
            Some(err) => DeleteAllDeadSessionsResponse::Err(err),
            None => DeleteAllDeadSessionsResponse::Ok,
        }
    }
}

impl From<GetPaneCwdResponse> for ProtobufGetPaneCwdResponse {
    fn from(response: GetPaneCwdResponse) -> Self {
        match response {
            GetPaneCwdResponse::Ok(path) => {
                let cwd_string = path
                    .to_str()
                    .map(|s| s.to_string())
                    .unwrap_or_else(|| String::from(""));
                ProtobufGetPaneCwdResponse {
                    result: Some(get_pane_cwd_response::Result::Cwd(cwd_string)),
                }
            },
            GetPaneCwdResponse::Err(err) => ProtobufGetPaneCwdResponse {
                result: Some(get_pane_cwd_response::Result::Error(err)),
            },
        }
    }
}

impl From<GetFocusedPaneInfoResponse> for ProtobufGetFocusedPaneInfoResponse {
    fn from(response: GetFocusedPaneInfoResponse) -> Self {
        match response {
            GetFocusedPaneInfoResponse::Ok { tab_index, pane_id } => {
                let protobuf_pane_id = ProtobufPaneId {
                    pane_type: match pane_id {
                        PaneId::Terminal(_) => ProtobufPaneType::Terminal as i32,
                        PaneId::Plugin(_) => ProtobufPaneType::Plugin as i32,
                    },
                    id: match pane_id {
                        PaneId::Terminal(id) | PaneId::Plugin(id) => id,
                    },
                };
                ProtobufGetFocusedPaneInfoResponse {
                    result: Some(get_focused_pane_info_response::Result::FocusedPaneInfo(
                        FocusedPaneInfo {
                            focused_tab_index: tab_index as u32,
                            focused_pane_id: Some(protobuf_pane_id),
                        },
                    )),
                }
            },
            GetFocusedPaneInfoResponse::Err(err) => ProtobufGetFocusedPaneInfoResponse {
                result: Some(get_focused_pane_info_response::Result::Error(err)),
            },
        }
    }
}

impl TryFrom<ProtobufSaveLayoutResponse> for SaveLayoutResponse {
    type Error = &'static str;
    fn try_from(protobuf_response: ProtobufSaveLayoutResponse) -> Result<Self, &'static str> {
        match protobuf_response.result {
            Some(save_layout_response::Result::Success(_)) => Ok(SaveLayoutResponse::Ok(())),
            Some(save_layout_response::Result::Error(error)) => Ok(SaveLayoutResponse::Err(error)),
            None => Err("Empty SaveLayoutResponse"),
        }
    }
}

impl From<SaveLayoutResponse> for ProtobufSaveLayoutResponse {
    fn from(response: SaveLayoutResponse) -> Self {
        match response {
            SaveLayoutResponse::Ok(_) => ProtobufSaveLayoutResponse {
                result: Some(save_layout_response::Result::Success(true)),
            },
            SaveLayoutResponse::Err(error) => ProtobufSaveLayoutResponse {
                result: Some(save_layout_response::Result::Error(error)),
            },
        }
    }
}

impl TryFrom<ProtobufDeleteLayoutResponse> for DeleteLayoutResponse {
    type Error = &'static str;
    fn try_from(protobuf_response: ProtobufDeleteLayoutResponse) -> Result<Self, &'static str> {
        match protobuf_response.result {
            Some(delete_layout_response::Result::Success(_)) => Ok(DeleteLayoutResponse::Ok(())),
            Some(delete_layout_response::Result::Error(error)) => {
                Ok(DeleteLayoutResponse::Err(error))
            },
            None => Err("Empty DeleteLayoutResponse"),
        }
    }
}

impl From<DeleteLayoutResponse> for ProtobufDeleteLayoutResponse {
    fn from(response: DeleteLayoutResponse) -> Self {
        match response {
            DeleteLayoutResponse::Ok(_) => ProtobufDeleteLayoutResponse {
                result: Some(delete_layout_response::Result::Success(true)),
            },
            DeleteLayoutResponse::Err(error) => ProtobufDeleteLayoutResponse {
                result: Some(delete_layout_response::Result::Error(error)),
            },
        }
    }
}

impl TryFrom<ProtobufRenameLayoutResponse> for RenameLayoutResponse {
    type Error = &'static str;
    fn try_from(protobuf_response: ProtobufRenameLayoutResponse) -> Result<Self, &'static str> {
        match protobuf_response.result {
            Some(rename_layout_response::Result::Success(_)) => Ok(RenameLayoutResponse::Ok(())),
            Some(rename_layout_response::Result::Error(error)) => {
                Ok(RenameLayoutResponse::Err(error))
            },
            None => Err("Empty RenameLayoutResponse"),
        }
    }
}

impl From<RenameLayoutResponse> for ProtobufRenameLayoutResponse {
    fn from(response: RenameLayoutResponse) -> Self {
        match response {
            RenameLayoutResponse::Ok(_) => ProtobufRenameLayoutResponse {
                result: Some(rename_layout_response::Result::Success(true)),
            },
            RenameLayoutResponse::Err(error) => ProtobufRenameLayoutResponse {
                result: Some(rename_layout_response::Result::Error(error)),
            },
        }
    }
}

impl TryFrom<ProtobufEditLayoutResponse> for EditLayoutResponse {
    type Error = &'static str;
    fn try_from(protobuf_response: ProtobufEditLayoutResponse) -> Result<Self, &'static str> {
        match protobuf_response.result {
            Some(edit_layout_response::Result::Success(_)) => Ok(EditLayoutResponse::Ok(())),
            Some(edit_layout_response::Result::Error(error)) => Ok(EditLayoutResponse::Err(error)),
            None => Err("Empty EditLayoutResponse"),
        }
    }
}

impl From<EditLayoutResponse> for ProtobufEditLayoutResponse {
    fn from(response: EditLayoutResponse) -> Self {
        match response {
            EditLayoutResponse::Ok(_) => ProtobufEditLayoutResponse {
                result: Some(edit_layout_response::Result::Success(true)),
            },
            EditLayoutResponse::Err(error) => ProtobufEditLayoutResponse {
                result: Some(edit_layout_response::Result::Error(error)),
            },
        }
    }
}

impl TryFrom<(InputMode, KeyWithModifier, Vec<Action>)> for KeyToRebind {
    type Error = &'static str;
    fn try_from(
        key_to_rebind: (InputMode, KeyWithModifier, Vec<Action>),
    ) -> Result<Self, &'static str> {
        Ok(KeyToRebind {
            input_mode: key_to_rebind.0 as i32,
            key: Some(key_to_rebind.1.try_into()?),
            actions: key_to_rebind
                .2
                .into_iter()
                .filter_map(|a| a.try_into().ok())
                .collect(),
        })
    }
}

impl TryFrom<(InputMode, KeyWithModifier)> for KeyToUnbind {
    type Error = &'static str;
    fn try_from(key_to_unbind: (InputMode, KeyWithModifier)) -> Result<Self, &'static str> {
        Ok(KeyToUnbind {
            input_mode: key_to_unbind.0 as i32,
            key: Some(key_to_unbind.1.try_into()?),
        })
    }
}

fn key_to_rebind_to_plugin_command_assets(
    key_to_rebind: KeyToRebind,
) -> Option<(InputMode, KeyWithModifier, Vec<Action>)> {
    Some((
        ProtobufInputMode::try_from(key_to_rebind.input_mode)
            .ok()?
            .try_into()
            .ok()?,
        key_to_rebind.key?.try_into().ok()?,
        key_to_rebind
            .actions
            .into_iter()
            .filter_map(|a| a.try_into().ok())
            .collect(),
    ))
}

fn key_to_unbind_to_plugin_command_assets(
    key_to_unbind: KeyToUnbind,
) -> Option<(InputMode, KeyWithModifier)> {
    Some((
        ProtobufInputMode::try_from(key_to_unbind.input_mode)
            .ok()?
            .try_into()
            .ok()?,
        key_to_unbind.key?.try_into().ok()?,
    ))
}

impl TryFrom<ProtobufPluginCommand> for PluginCommand {
    type Error = &'static str;
    fn try_from(protobuf_plugin_command: ProtobufPluginCommand) -> Result<Self, &'static str> {
        match CommandName::try_from(protobuf_plugin_command.name).ok() {
            Some(CommandName::Subscribe) => match protobuf_plugin_command.payload {
                Some(Payload::SubscribePayload(subscribe_payload)) => {
                    let protobuf_event_list = subscribe_payload.subscriptions;
                    match protobuf_event_list {
                        Some(protobuf_event_list) => {
                            Ok(PluginCommand::Subscribe(protobuf_event_list.try_into()?))
                        },
                        None => Err("malformed subscription event"),
                    }
                },
                _ => Err("Mismatched payload for Subscribe"),
            },
            Some(CommandName::Unsubscribe) => match protobuf_plugin_command.payload {
                Some(Payload::UnsubscribePayload(unsubscribe_payload)) => {
                    let protobuf_event_list = unsubscribe_payload.subscriptions;
                    match protobuf_event_list {
                        Some(protobuf_event_list) => {
                            Ok(PluginCommand::Unsubscribe(protobuf_event_list.try_into()?))
                        },
                        None => Err("malformed unsubscription event"),
                    }
                },
                _ => Err("Mismatched payload for Unsubscribe"),
            },
            Some(CommandName::SetSelectable) => match protobuf_plugin_command.payload {
                Some(Payload::SetSelectablePayload(should_be_selectable)) => {
                    Ok(PluginCommand::SetSelectable(should_be_selectable))
                },
                _ => Err("Mismatched payload for SetSelectable"),
            },
            Some(CommandName::ShowCursor) => match protobuf_plugin_command.payload {
                Some(Payload::ShowCursorPayload(payload)) => {
                    let cursor_position =
                        payload.position.map(|pos| (pos.x as usize, pos.y as usize));
                    Ok(PluginCommand::ShowCursor(cursor_position))
                },
                _ => Err("Mismatched payload for ShowCursor"),
            },
            Some(CommandName::GetPluginIds) => {
                if protobuf_plugin_command.payload.is_some() {
                    Err("GetPluginIds should not have a payload")
                } else {
                    Ok(PluginCommand::GetPluginIds)
                }
            },
            Some(CommandName::GetZellijVersion) => {
                if protobuf_plugin_command.payload.is_some() {
                    Err("GetZellijVersion should not have a payload")
                } else {
                    Ok(PluginCommand::GetZellijVersion)
                }
            },
            Some(CommandName::OpenFile) => match protobuf_plugin_command.payload {
                Some(Payload::OpenFilePayload(file_to_open_payload)) => {
                    match file_to_open_payload.file_to_open {
                        Some(file_to_open) => {
                            let context: BTreeMap<String, String> = file_to_open_payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenFile(file_to_open.try_into()?, context))
                        },
                        None => Err("Malformed open file payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenFile"),
            },
            Some(CommandName::OpenFileFloating) => match protobuf_plugin_command.payload {
                Some(Payload::OpenFileFloatingPayload(file_to_open_payload)) => {
                    let floating_pane_coordinates = file_to_open_payload
                        .floating_pane_coordinates
                        .map(|f| f.into());
                    let context: BTreeMap<String, String> = file_to_open_payload
                        .context
                        .into_iter()
                        .map(|e| (e.name, e.value))
                        .collect();
                    match file_to_open_payload.file_to_open {
                        Some(file_to_open) => Ok(PluginCommand::OpenFileFloating(
                            file_to_open.try_into()?,
                            floating_pane_coordinates,
                            context,
                        )),
                        None => Err("Malformed open file payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenFileFloating"),
            },
            Some(CommandName::OpenTerminal) => match protobuf_plugin_command.payload {
                Some(Payload::OpenTerminalPayload(file_to_open_payload)) => {
                    match file_to_open_payload.file_to_open {
                        Some(file_to_open) => {
                            Ok(PluginCommand::OpenTerminal(file_to_open.try_into()?))
                        },
                        None => Err("Malformed open terminal payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenTerminal"),
            },
            Some(CommandName::OpenTerminalFloating) => match protobuf_plugin_command.payload {
                Some(Payload::OpenTerminalFloatingPayload(file_to_open_payload)) => {
                    let floating_pane_coordinates = file_to_open_payload
                        .floating_pane_coordinates
                        .map(|f| f.into());
                    match file_to_open_payload.file_to_open {
                        Some(file_to_open) => Ok(PluginCommand::OpenTerminalFloating(
                            file_to_open.try_into()?,
                            floating_pane_coordinates,
                        )),
                        None => Err("Malformed open terminal floating payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenTerminalFloating"),
            },
            Some(CommandName::OpenCommandPane) => match protobuf_plugin_command.payload {
                Some(Payload::OpenCommandPanePayload(command_to_run_payload)) => {
                    match command_to_run_payload.command_to_run {
                        Some(command_to_run) => {
                            let context: BTreeMap<String, String> = command_to_run_payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenCommandPane(
                                command_to_run.try_into()?,
                                context,
                            ))
                        },
                        None => Err("Malformed open open command pane payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenCommandPane"),
            },
            Some(CommandName::OpenCommandPaneFloating) => match protobuf_plugin_command.payload {
                Some(Payload::OpenCommandPaneFloatingPayload(command_to_run_payload)) => {
                    let floating_pane_coordinates = command_to_run_payload
                        .floating_pane_coordinates
                        .map(|f| f.into());
                    match command_to_run_payload.command_to_run {
                        Some(command_to_run) => {
                            let context: BTreeMap<String, String> = command_to_run_payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenCommandPaneFloating(
                                command_to_run.try_into()?,
                                floating_pane_coordinates,
                                context,
                            ))
                        },
                        None => Err("Malformed open command pane floating payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenCommandPaneFloating"),
            },
            Some(CommandName::SwitchTabTo) => match protobuf_plugin_command.payload {
                Some(Payload::SwitchTabToPayload(switch_to_tab_payload)) => Ok(
                    PluginCommand::SwitchTabTo(switch_to_tab_payload.tab_index as u32),
                ),
                _ => Err("Mismatched payload for SwitchToTab"),
            },
            Some(CommandName::SetTimeout) => match protobuf_plugin_command.payload {
                Some(Payload::SetTimeoutPayload(set_timeout_payload)) => {
                    Ok(PluginCommand::SetTimeout(set_timeout_payload.seconds))
                },
                _ => Err("Mismatched payload for SetTimeout"),
            },
            Some(CommandName::ExecCmd) => match protobuf_plugin_command.payload {
                Some(Payload::ExecCmdPayload(exec_cmd_payload)) => {
                    Ok(PluginCommand::ExecCmd(exec_cmd_payload.command_line))
                },
                _ => Err("Mismatched payload for ExecCmd"),
            },
            Some(CommandName::PostMessageTo) => match protobuf_plugin_command.payload {
                Some(Payload::PostMessageToPayload(post_message_to_payload)) => {
                    match post_message_to_payload.message {
                        Some(message) => Ok(PluginCommand::PostMessageTo(message.try_into()?)),
                        None => Err("Malformed post message to payload"),
                    }
                },
                _ => Err("Mismatched payload for PostMessageTo"),
            },
            Some(CommandName::PostMessageToPlugin) => match protobuf_plugin_command.payload {
                Some(Payload::PostMessageToPluginPayload(post_message_to_payload)) => {
                    match post_message_to_payload.message {
                        Some(message) => {
                            Ok(PluginCommand::PostMessageToPlugin(message.try_into()?))
                        },
                        None => Err("Malformed post message to plugin payload"),
                    }
                },
                _ => Err("Mismatched payload for PostMessageToPlugin"),
            },
            Some(CommandName::HideSelf) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("HideSelf should not have a payload");
                }
                Ok(PluginCommand::HideSelf)
            },
            Some(CommandName::ShowSelf) => match protobuf_plugin_command.payload {
                Some(Payload::ShowSelfPayload(should_float_if_hidden)) => {
                    Ok(PluginCommand::ShowSelf(should_float_if_hidden))
                },
                _ => Err("Mismatched payload for ShowSelf"),
            },
            Some(CommandName::SwitchToMode) => match protobuf_plugin_command.payload {
                Some(Payload::SwitchToModePayload(switch_to_mode_payload)) => {
                    match ProtobufInputMode::try_from(switch_to_mode_payload.input_mode).ok() {
                        Some(protobuf_input_mode) => {
                            Ok(PluginCommand::SwitchToMode(protobuf_input_mode.try_into()?))
                        },
                        None => Err("Malformed switch to mode payload"),
                    }
                },
                _ => Err("Mismatched payload for SwitchToMode"),
            },
            Some(CommandName::NewTabsWithLayout) => match protobuf_plugin_command.payload {
                Some(Payload::NewTabsWithLayoutPayload(raw_layout)) => {
                    Ok(PluginCommand::NewTabsWithLayout(raw_layout))
                },
                _ => Err("Mismatched payload for NewTabsWithLayout"),
            },
            Some(CommandName::NewTab) => match protobuf_plugin_command.payload {
                Some(Payload::NewTabPayload(protobuf_new_tab_payload)) => {
                    Ok(PluginCommand::NewTab {
                        name: protobuf_new_tab_payload.name,
                        cwd: protobuf_new_tab_payload.cwd,
                    })
                },
                None => Ok(PluginCommand::NewTab {
                    name: None,
                    cwd: None,
                }),
                _ => Err("Mismatched payload for NewTab"),
            },
            Some(CommandName::NewTabUnfocused) => match protobuf_plugin_command.payload {
                Some(Payload::NewTabUnfocusedPayload(payload)) => {
                    Ok(PluginCommand::NewTabUnfocused {
                        name: payload.name,
                        cwd: payload.cwd,
                    })
                },
                None => Ok(PluginCommand::NewTabUnfocused {
                    name: None,
                    cwd: None,
                }),
                _ => Err("Mismatched payload for NewTabUnfocused"),
            },
            Some(CommandName::NewTiledPaneInTab) => match protobuf_plugin_command.payload {
                Some(Payload::NewTiledPaneInTabPayload(payload)) => {
                    Ok(PluginCommand::NewTiledPaneInTab {
                        tab_position: payload.tab_position as usize,
                    })
                },
                _ => Err("Mismatched payload for NewTiledPaneInTab"),
            },
            Some(CommandName::ToggleFloatingPanes) => match protobuf_plugin_command.payload {
                Some(Payload::ToggleFloatingPanesPayload(payload)) => {
                    Ok(PluginCommand::ToggleFloatingPanes {
                        tab_id: payload.tab_id,
                    })
                },
                _ => Ok(PluginCommand::ToggleFloatingPanes { tab_id: None }),
            },
            Some(CommandName::NewPane) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("NewPane should not have a payload");
                }
                Ok(PluginCommand::NewPane)
            },
            Some(CommandName::GoToNextTab) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("GoToNextTab should not have a payload");
                }
                Ok(PluginCommand::GoToNextTab)
            },
            Some(CommandName::GoToPreviousTab) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("GoToPreviousTab should not have a payload");
                }
                Ok(PluginCommand::GoToPreviousTab)
            },
            Some(CommandName::Resize) => match protobuf_plugin_command.payload {
                Some(Payload::ResizePayload(resize_payload)) => match resize_payload.resize {
                    Some(resize) => Ok(PluginCommand::Resize(resize.try_into()?)),
                    None => Err("Malformed switch resize payload"),
                },
                _ => Err("Mismatched payload for Resize"),
            },
            Some(CommandName::ResizeWithDirection) => match protobuf_plugin_command.payload {
                Some(Payload::ResizeWithDirectionPayload(resize_with_direction_payload)) => {
                    match resize_with_direction_payload.resize {
                        Some(resize) => Ok(PluginCommand::ResizeWithDirection(resize.try_into()?)),
                        None => Err("Malformed switch resize payload"),
                    }
                },
                _ => Err("Mismatched payload for Resize"),
            },
            Some(CommandName::FocusNextPane) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("FocusNextPane should not have a payload");
                }
                Ok(PluginCommand::FocusNextPane)
            },
            Some(CommandName::FocusPreviousPane) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("FocusPreviousPane should not have a payload");
                }
                Ok(PluginCommand::FocusPreviousPane)
            },
            Some(CommandName::FocusLastPane) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("FocusLastPane should not have a payload");
                }
                Ok(PluginCommand::FocusLastPane)
            },
            Some(CommandName::MoveFocus) => match protobuf_plugin_command.payload {
                Some(Payload::MoveFocusPayload(move_payload)) => match move_payload.direction {
                    Some(direction) => Ok(PluginCommand::MoveFocus(direction.try_into()?)),
                    None => Err("Malformed move focus payload"),
                },
                _ => Err("Mismatched payload for MoveFocus"),
            },
            Some(CommandName::MoveFocusOrTab) => match protobuf_plugin_command.payload {
                Some(Payload::MoveFocusOrTabPayload(move_payload)) => {
                    match move_payload.direction {
                        Some(direction) => Ok(PluginCommand::MoveFocusOrTab(direction.try_into()?)),
                        None => Err("Malformed move focus or tab payload"),
                    }
                },
                _ => Err("Mismatched payload for MoveFocusOrTab"),
            },
            Some(CommandName::Detach) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("Detach should not have a payload");
                }
                Ok(PluginCommand::Detach)
            },
            Some(CommandName::EditScrollback) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("EditScrollback should not have a payload");
                }
                Ok(PluginCommand::EditScrollback)
            },
            Some(CommandName::Write) => match protobuf_plugin_command.payload {
                Some(Payload::WritePayload(bytes)) => Ok(PluginCommand::Write(bytes)),
                _ => Err("Mismatched payload for Write"),
            },
            Some(CommandName::WriteChars) => match protobuf_plugin_command.payload {
                Some(Payload::WriteCharsPayload(chars)) => Ok(PluginCommand::WriteChars(chars)),
                _ => Err("Mismatched payload for WriteChars"),
            },
            Some(CommandName::ToggleTab) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("ToggleTab should not have a payload");
                }
                Ok(PluginCommand::ToggleTab)
            },
            Some(CommandName::MovePane) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("MovePane should not have a payload");
                }
                Ok(PluginCommand::MovePane)
            },
            Some(CommandName::MovePaneWithDirection) => match protobuf_plugin_command.payload {
                Some(Payload::MovePaneWithDirectionPayload(move_payload)) => {
                    match move_payload.direction {
                        Some(direction) => {
                            Ok(PluginCommand::MovePaneWithDirection(direction.try_into()?))
                        },
                        None => Err("Malformed MovePaneWithDirection payload"),
                    }
                },
                _ => Err("Mismatched payload for MovePaneWithDirection"),
            },
            Some(CommandName::ClearScreen) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("ClearScreen should not have a payload");
                }
                Ok(PluginCommand::ClearScreen)
            },
            Some(CommandName::ScrollUp) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("ScrollUp should not have a payload");
                }
                Ok(PluginCommand::ScrollUp)
            },
            Some(CommandName::ScrollDown) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("ScrollDown should not have a payload");
                }
                Ok(PluginCommand::ScrollDown)
            },
            Some(CommandName::ScrollToTop) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("ScrollToTop should not have a payload");
                }
                Ok(PluginCommand::ScrollToTop)
            },
            Some(CommandName::ScrollToBottom) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("ScrollToBottom should not have a payload");
                }
                Ok(PluginCommand::ScrollToBottom)
            },
            Some(CommandName::PageScrollUp) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("PageScrollUp should not have a payload");
                }
                Ok(PluginCommand::PageScrollUp)
            },
            Some(CommandName::PageScrollDown) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("PageScrollDown should not have a payload");
                }
                Ok(PluginCommand::PageScrollDown)
            },
            Some(CommandName::ToggleFocusFullscreen) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("ToggleFocusFullscreen should not have a payload");
                }
                Ok(PluginCommand::ToggleFocusFullscreen)
            },
            Some(CommandName::ToggleFocusNoUiFullscreen) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("ToggleFocusNoUiFullscreen should not have a payload");
                }
                Ok(PluginCommand::ToggleFocusNoUiFullscreen)
            },
            Some(CommandName::FocusHostSession) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("FocusHostSession should not have a payload");
                }
                Ok(PluginCommand::FocusHostSession)
            },
            Some(CommandName::TogglePaneFrames) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("TogglePaneFrames should not have a payload");
                }
                Ok(PluginCommand::TogglePaneFrames)
            },
            Some(CommandName::SetPaneFrameStyle) => match protobuf_plugin_command.payload {
                Some(Payload::SetPaneFrameStylePayload(payload)) => {
                    match ProtobufPaneFrameStyle::try_from(payload.pane_frame_style).ok() {
                        Some(protobuf_pane_frame_style) => Ok(PluginCommand::SetPaneFrameStyle(
                            protobuf_pane_frame_style.try_into()?,
                        )),
                        None => Err("Malformed SetPaneFrameStyle payload"),
                    }
                },
                _ => Err("Mismatched payload for SetPaneFrameStyle"),
            },
            Some(CommandName::TogglePaneEmbedOrEject) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("TogglePaneEmbedOrEject should not have a payload");
                }
                Ok(PluginCommand::TogglePaneEmbedOrEject)
            },
            Some(CommandName::UndoRenamePane) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("UndoRenamePane should not have a payload");
                }
                Ok(PluginCommand::UndoRenamePane)
            },
            Some(CommandName::CloseFocus) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("CloseFocus should not have a payload");
                }
                Ok(PluginCommand::CloseFocus)
            },
            Some(CommandName::ToggleActiveTabSync) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("ToggleActiveTabSync should not have a payload");
                }
                Ok(PluginCommand::ToggleActiveTabSync)
            },
            Some(CommandName::CloseFocusedTab) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("CloseFocusedTab should not have a payload");
                }
                Ok(PluginCommand::CloseFocusedTab)
            },
            Some(CommandName::UndoRenameTab) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("UndoRenameTab should not have a payload");
                }
                Ok(PluginCommand::UndoRenameTab)
            },
            Some(CommandName::QuitZellij) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("QuitZellij should not have a payload");
                }
                Ok(PluginCommand::QuitZellij)
            },
            Some(CommandName::PreviousSwapLayout) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("PreviousSwapLayout should not have a payload");
                }
                Ok(PluginCommand::PreviousSwapLayout)
            },
            Some(CommandName::NextSwapLayout) => {
                if protobuf_plugin_command.payload.is_some() {
                    return Err("NextSwapLayout should not have a payload");
                }
                Ok(PluginCommand::NextSwapLayout)
            },
            Some(CommandName::GoToTabName) => match protobuf_plugin_command.payload {
                Some(Payload::GoToTabNamePayload(tab_name)) => {
                    Ok(PluginCommand::GoToTabName(tab_name))
                },
                _ => Err("Mismatched payload for GoToTabName"),
            },
            Some(CommandName::FocusOrCreateTab) => match protobuf_plugin_command.payload {
                Some(Payload::FocusOrCreateTabPayload(tab_name)) => {
                    Ok(PluginCommand::FocusOrCreateTab(tab_name))
                },
                _ => Err("Mismatched payload for FocusOrCreateTab"),
            },
            Some(CommandName::GoToTab) => match protobuf_plugin_command.payload {
                Some(Payload::GoToTabPayload(tab_index)) => {
                    Ok(PluginCommand::GoToTab(tab_index as u32))
                },
                _ => Err("Mismatched payload for GoToTab"),
            },
            Some(CommandName::StartOrReloadPlugin) => match protobuf_plugin_command.payload {
                Some(Payload::StartOrReloadPluginPayload(url)) => {
                    Ok(PluginCommand::StartOrReloadPlugin(url))
                },
                _ => Err("Mismatched payload for StartOrReloadPlugin"),
            },
            Some(CommandName::CloseTerminalPane) => match protobuf_plugin_command.payload {
                Some(Payload::CloseTerminalPanePayload(pane_id)) => {
                    Ok(PluginCommand::CloseTerminalPane(pane_id as u32))
                },
                _ => Err("Mismatched payload for CloseTerminalPane"),
            },
            Some(CommandName::ClosePluginPane) => match protobuf_plugin_command.payload {
                Some(Payload::ClosePluginPanePayload(pane_id)) => {
                    Ok(PluginCommand::ClosePluginPane(pane_id as u32))
                },
                _ => Err("Mismatched payload for ClosePluginPane"),
            },
            Some(CommandName::FocusTerminalPane) => match protobuf_plugin_command.payload {
                Some(Payload::FocusTerminalPanePayload(payload)) => {
                    let pane_id = payload.pane_id as u32;
                    let should_float = payload.should_float;
                    let should_be_in_place = payload.should_be_in_place;
                    Ok(PluginCommand::FocusTerminalPane(
                        pane_id,
                        should_float,
                        should_be_in_place,
                    ))
                },
                _ => Err("Mismatched payload for ClosePluginPane"),
            },
            Some(CommandName::FocusPluginPane) => match protobuf_plugin_command.payload {
                Some(Payload::FocusPluginPanePayload(payload)) => {
                    let pane_id = payload.pane_id as u32;
                    let should_float = payload.should_float;
                    let should_be_in_place = payload.should_be_in_place;
                    Ok(PluginCommand::FocusPluginPane(
                        pane_id,
                        should_float,
                        should_be_in_place,
                    ))
                },
                _ => Err("Mismatched payload for ClosePluginPane"),
            },
            Some(CommandName::RenameTerminalPane) => match protobuf_plugin_command.payload {
                Some(Payload::RenameTerminalPanePayload(payload)) => {
                    let pane_id = payload.id as u32;
                    let new_name = payload.new_name;
                    Ok(PluginCommand::RenameTerminalPane(pane_id, new_name))
                },
                _ => Err("Mismatched payload for RenameTerminalPane"),
            },
            Some(CommandName::RenamePluginPane) => match protobuf_plugin_command.payload {
                Some(Payload::RenamePluginPanePayload(payload)) => {
                    let pane_id = payload.id as u32;
                    let new_name = payload.new_name;
                    Ok(PluginCommand::RenamePluginPane(pane_id, new_name))
                },
                _ => Err("Mismatched payload for RenamePluginPane"),
            },
            Some(CommandName::RenameTab) => match protobuf_plugin_command.payload {
                Some(Payload::RenameTabPayload(payload)) => {
                    let tab_index = payload.id as u32;
                    let name = payload.new_name;
                    Ok(PluginCommand::RenameTab(tab_index, name))
                },
                _ => Err("Mismatched payload for RenameTab"),
            },
            Some(CommandName::ReportCrash) => match protobuf_plugin_command.payload {
                Some(Payload::ReportCrashPayload(payload)) => {
                    Ok(PluginCommand::ReportPanic(payload))
                },
                _ => Err("Mismatched payload for ReportCrash"),
            },
            Some(CommandName::RequestPluginPermissions) => match protobuf_plugin_command.payload {
                Some(Payload::RequestPluginPermissionPayload(payload)) => {
                    Ok(PluginCommand::RequestPluginPermissions(
                        payload
                            .permissions
                            .iter()
                            .filter_map(|p| ProtobufPermissionType::try_from(*p).ok())
                            .filter_map(|p| PermissionType::try_from(p).ok())
                            .collect(),
                    ))
                },
                _ => Err("Mismatched payload for RequestPluginPermission"),
            },
            Some(CommandName::SwitchSession) => match protobuf_plugin_command.payload {
                Some(Payload::SwitchSessionPayload(payload)) => {
                    let pane_id = match (payload.pane_id, payload.pane_id_is_plugin) {
                        (Some(pane_id), Some(is_plugin)) => Some((pane_id, is_plugin)),
                        (None, None) => None,
                        _ => {
                            return Err("Malformed payload for SwitchSession, 'pane_id' and 'is_plugin' must be included together or not at all")
                        }
                    };
                    Ok(PluginCommand::SwitchSession(ConnectToSession {
                        name: payload.name,
                        tab_position: payload.tab_position.map(|p| p as usize),
                        pane_id,
                        layout: payload.layout.and_then(|l| l.try_into().ok()),
                        cwd: payload.cwd.map(|c| PathBuf::from(c)),
                    }))
                },
                _ => Err("Mismatched payload for SwitchSession"),
            },
            Some(CommandName::OpenTerminalInPlace) => match protobuf_plugin_command.payload {
                Some(Payload::OpenTerminalInPlacePayload(file_to_open_payload)) => {
                    match file_to_open_payload.file_to_open {
                        Some(file_to_open) => {
                            Ok(PluginCommand::OpenTerminalInPlace(file_to_open.try_into()?))
                        },
                        None => Err("Malformed open terminal in-place payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenTerminalInPlace"),
            },
            Some(CommandName::OpenFileInPlace) => match protobuf_plugin_command.payload {
                Some(Payload::OpenFileInPlacePayload(file_to_open_payload)) => {
                    match file_to_open_payload.file_to_open {
                        Some(file_to_open) => {
                            let context: BTreeMap<String, String> = file_to_open_payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenFileInPlace(
                                file_to_open.try_into()?,
                                context,
                            ))
                        },
                        None => Err("Malformed open file in place payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenFileInPlace"),
            },
            Some(CommandName::OpenCommandInPlace) => match protobuf_plugin_command.payload {
                Some(Payload::OpenCommandPaneInPlacePayload(command_to_run_payload)) => {
                    match command_to_run_payload.command_to_run {
                        Some(command_to_run) => {
                            let context: BTreeMap<String, String> = command_to_run_payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenCommandPaneInPlace(
                                command_to_run.try_into()?,
                                context,
                            ))
                        },
                        None => Err("Malformed open command pane in-place payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenCommandPaneInPlace"),
            },
            Some(CommandName::RunCommand) => match protobuf_plugin_command.payload {
                Some(Payload::RunCommandPayload(run_command_payload)) => {
                    let env_variables: BTreeMap<String, String> = run_command_payload
                        .env_variables
                        .into_iter()
                        .map(|e| (e.name, e.value))
                        .collect();
                    let context: BTreeMap<String, String> = run_command_payload
                        .context
                        .into_iter()
                        .map(|e| (e.name, e.value))
                        .collect();
                    Ok(PluginCommand::RunCommand(
                        run_command_payload.command_line,
                        env_variables,
                        PathBuf::from(run_command_payload.cwd),
                        context,
                    ))
                },
                _ => Err("Mismatched payload for RunCommand"),
            },
            Some(CommandName::WebRequest) => match protobuf_plugin_command.payload {
                Some(Payload::WebRequestPayload(web_request_payload)) => {
                    let context: BTreeMap<String, String> = web_request_payload
                        .context
                        .into_iter()
                        .map(|e| (e.name, e.value))
                        .collect();
                    let headers: BTreeMap<String, String> = web_request_payload
                        .headers
                        .into_iter()
                        .map(|e| (e.name, e.value))
                        .collect();
                    let verb = match ProtobufHttpVerb::try_from(web_request_payload.verb).ok() {
                        Some(verb) => verb.into(),
                        None => {
                            return Err("Unrecognized http verb");
                        },
                    };
                    Ok(PluginCommand::WebRequest(
                        web_request_payload.url,
                        verb,
                        headers,
                        web_request_payload.body,
                        context,
                    ))
                },
                _ => Err("Mismatched payload for WebRequest"),
            },
            Some(CommandName::DeleteDeadSession) => match protobuf_plugin_command.payload {
                Some(Payload::DeleteDeadSessionPayload(dead_session_name)) => {
                    Ok(PluginCommand::DeleteDeadSession(dead_session_name))
                },
                _ => Err("Mismatched payload for DeleteDeadSession"),
            },
            Some(CommandName::DeleteAllDeadSessions) => Ok(PluginCommand::DeleteAllDeadSessions),
            Some(CommandName::RenameSession) => match protobuf_plugin_command.payload {
                Some(Payload::RenameSessionPayload(new_session_name)) => {
                    Ok(PluginCommand::RenameSession(new_session_name))
                },
                _ => Err("Mismatched payload for RenameSession"),
            },
            Some(CommandName::UnblockCliPipeInput) => match protobuf_plugin_command.payload {
                Some(Payload::UnblockCliPipeInputPayload(pipe_name)) => {
                    Ok(PluginCommand::UnblockCliPipeInput(pipe_name))
                },
                _ => Err("Mismatched payload for UnblockPipeInput"),
            },
            Some(CommandName::BlockCliPipeInput) => match protobuf_plugin_command.payload {
                Some(Payload::BlockCliPipeInputPayload(pipe_name)) => {
                    Ok(PluginCommand::BlockCliPipeInput(pipe_name))
                },
                _ => Err("Mismatched payload for BlockPipeInput"),
            },
            Some(CommandName::CliPipeOutput) => match protobuf_plugin_command.payload {
                Some(Payload::CliPipeOutputPayload(CliPipeOutputPayload { pipe_name, output })) => {
                    Ok(PluginCommand::CliPipeOutput(pipe_name, output))
                },
                _ => Err("Mismatched payload for PipeOutput"),
            },
            Some(CommandName::MessageToPlugin) => match protobuf_plugin_command.payload {
                Some(Payload::MessageToPluginPayload(MessageToPluginPayload {
                    plugin_url,
                    plugin_config,
                    message_name,
                    message_payload,
                    message_args,
                    new_plugin_args,
                    destination_plugin_id,
                    floating_pane_coordinates,
                })) => {
                    let plugin_config: BTreeMap<String, String> = plugin_config
                        .into_iter()
                        .map(|e| (e.name, e.value))
                        .collect();
                    let message_args: BTreeMap<String, String> = message_args
                        .into_iter()
                        .map(|e| (e.name, e.value))
                        .collect();
                    Ok(PluginCommand::MessageToPlugin(MessageToPlugin {
                        plugin_url,
                        plugin_config,
                        message_name,
                        message_payload,
                        message_args,
                        new_plugin_args: new_plugin_args.and_then(|protobuf_new_plugin_args| {
                            Some(NewPluginArgs {
                                should_float: protobuf_new_plugin_args.should_float,
                                pane_id_to_replace: protobuf_new_plugin_args
                                    .pane_id_to_replace
                                    .and_then(|p_id| PaneId::try_from(p_id).ok()),
                                pane_title: protobuf_new_plugin_args.pane_title,
                                cwd: protobuf_new_plugin_args.cwd.map(|cwd| PathBuf::from(cwd)),
                                skip_cache: protobuf_new_plugin_args.skip_cache,
                                should_focus: protobuf_new_plugin_args.should_focus,
                            })
                        }),
                        destination_plugin_id,
                        floating_pane_coordinates: floating_pane_coordinates
                            .and_then(|f| f.try_into().ok()),
                    }))
                },
                _ => Err("Mismatched payload for MessageToPlugin"),
            },
            Some(CommandName::DisconnectOtherClients) => match protobuf_plugin_command.payload {
                None => Ok(PluginCommand::DisconnectOtherClients),
                _ => Err("Mismatched payload for DisconnectOtherClients"),
            },
            Some(CommandName::KillSessions) => match protobuf_plugin_command.payload {
                Some(Payload::KillSessionsPayload(KillSessionsPayload { session_names })) => {
                    Ok(PluginCommand::KillSessions(session_names))
                },
                _ => Err("Mismatched payload for KillSessions"),
            },
            Some(CommandName::ScanHostFolder) => match protobuf_plugin_command.payload {
                Some(Payload::ScanHostFolderPayload(folder_to_scan)) => {
                    Ok(PluginCommand::ScanHostFolder(PathBuf::from(folder_to_scan)))
                },
                _ => Err("Mismatched payload for ScanHostFolder"),
            },
            Some(CommandName::WatchFilesystem) => match protobuf_plugin_command.payload {
                Some(_) => Err("WatchFilesystem should have no payload, found a payload"),
                None => Ok(PluginCommand::WatchFilesystem),
            },
            Some(CommandName::ListWindowsVolumes) => match protobuf_plugin_command.payload {
                Some(_) => Err("ListWindowsVolumes should have no payload"),
                None => Ok(PluginCommand::ListWindowsVolumes),
            },
            Some(CommandName::GetSessionList) => match protobuf_plugin_command.payload {
                Some(Payload::GetSessionListPayload(_)) => Ok(PluginCommand::GetSessionList),
                None => Ok(PluginCommand::GetSessionList),
                _ => Err("Mismatched payload for GetSessionList"),
            },
            Some(CommandName::KillSessionsAndReply) => match protobuf_plugin_command.payload {
                Some(Payload::KillSessionsAndReplyPayload(KillSessionsPayload {
                    session_names,
                })) => Ok(PluginCommand::KillSessionsAndReply(session_names)),
                _ => Err("Mismatched payload for KillSessionsAndReply"),
            },
            Some(CommandName::DeleteDeadSessionAndReply) => match protobuf_plugin_command.payload {
                Some(Payload::DeleteDeadSessionAndReplyPayload(dead_session_name)) => {
                    Ok(PluginCommand::DeleteDeadSessionAndReply(dead_session_name))
                },
                _ => Err("Mismatched payload for DeleteDeadSessionAndReply"),
            },
            Some(CommandName::DeleteAllDeadSessionsAndReply) => {
                Ok(PluginCommand::DeleteAllDeadSessionsAndReply)
            },
            Some(CommandName::SetSoftKeyboard) => match protobuf_plugin_command.payload {
                Some(Payload::SetSoftKeyboardPayload(payload)) => {
                    Ok(PluginCommand::SetSoftKeyboard(payload.on))
                },
                _ => Err("Mismatched payload for SetSoftKeyboard"),
            },
            Some(CommandName::DumpSessionLayout) => match protobuf_plugin_command.payload {
                Some(Payload::DumpSessionLayoutPayload(payload)) => {
                    Ok(PluginCommand::DumpSessionLayout {
                        tab_index: payload.tab_index.map(|i| i as usize),
                    })
                },
                None => Ok(PluginCommand::DumpSessionLayout { tab_index: None }),
                _ => Err("Mismatched payload for DumpSessionLayout"),
            },
            Some(CommandName::CloseSelf) => match protobuf_plugin_command.payload {
                Some(_) => Err("CloseSelf should have no payload, found a payload"),
                None => Ok(PluginCommand::CloseSelf),
            },
            Some(CommandName::NewTabsWithLayoutInfo) => match protobuf_plugin_command.payload {
                Some(Payload::NewTabsWithLayoutInfoPayload(new_tabs_with_layout_info_payload)) => {
                    new_tabs_with_layout_info_payload
                        .layout_info
                        .and_then(|layout_info| {
                            Some(PluginCommand::NewTabsWithLayoutInfo(
                                layout_info.try_into().ok()?,
                            ))
                        })
                        .ok_or("Failed to parse NewTabsWithLayoutInfo command")
                },
                _ => Err("Mismatched payload for NewTabsWithLayoutInfo"),
            },
            Some(CommandName::Reconfigure) => match protobuf_plugin_command.payload {
                Some(Payload::ReconfigurePayload(reconfigure_payload)) => {
                    Ok(PluginCommand::Reconfigure(
                        reconfigure_payload.config,
                        reconfigure_payload.write_to_disk,
                    ))
                },
                _ => Err("Mismatched payload for Reconfigure"),
            },
            Some(CommandName::HidePaneWithId) => match protobuf_plugin_command.payload {
                Some(Payload::HidePaneWithIdPayload(hide_pane_with_id_payload)) => {
                    let pane_id = hide_pane_with_id_payload
                        .pane_id
                        .and_then(|p_id| PaneId::try_from(p_id).ok())
                        .ok_or("Failed to parse HidePaneWithId command")?;
                    Ok(PluginCommand::HidePaneWithId(pane_id))
                },
                _ => Err("Mismatched payload for HidePaneWithId"),
            },
            Some(CommandName::ShowPaneWithId) => match protobuf_plugin_command.payload {
                Some(Payload::ShowPaneWithIdPayload(show_pane_with_id_payload)) => {
                    let pane_id = show_pane_with_id_payload
                        .pane_id
                        .and_then(|p_id| PaneId::try_from(p_id).ok())
                        .ok_or("Failed to parse ShowPaneWithId command")?;
                    let should_float_if_hidden = show_pane_with_id_payload.should_float_if_hidden;
                    let should_focus_pane = show_pane_with_id_payload.should_focus_pane;
                    Ok(PluginCommand::ShowPaneWithId(
                        pane_id,
                        should_float_if_hidden,
                        should_focus_pane,
                    ))
                },
                _ => Err("Mismatched payload for ShowPaneWithId"),
            },
            Some(CommandName::OpenCommandPaneBackground) => match protobuf_plugin_command.payload {
                Some(Payload::OpenCommandPaneBackgroundPayload(command_to_run_payload)) => {
                    match command_to_run_payload.command_to_run {
                        Some(command_to_run) => {
                            let context: BTreeMap<String, String> = command_to_run_payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenCommandPaneBackground(
                                command_to_run.try_into()?,
                                context,
                            ))
                        },
                        None => Err("Malformed open command pane background payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenCommandPaneBackground"),
            },
            Some(CommandName::RerunCommandPane) => match protobuf_plugin_command.payload {
                Some(Payload::RerunCommandPanePayload(rerun_command_pane_payload)) => Ok(
                    PluginCommand::RerunCommandPane(rerun_command_pane_payload.terminal_pane_id),
                ),
                _ => Err("Mismatched payload for RerunCommandPane"),
            },
            Some(CommandName::ResizePaneIdWithDirection) => match protobuf_plugin_command.payload {
                Some(Payload::ResizePaneIdWithDirectionPayload(resize_with_direction_payload)) => {
                    match (
                        resize_with_direction_payload.resize,
                        resize_with_direction_payload.pane_id,
                    ) {
                        (Some(resize), Some(pane_id)) => {
                            Ok(PluginCommand::ResizePaneIdWithDirection(
                                resize.try_into()?,
                                pane_id.try_into()?,
                            ))
                        },
                        _ => Err("Malformed resize_pane_with_id payload"),
                    }
                },
                _ => Err("Mismatched payload for Resize"),
            },
            Some(CommandName::EditScrollbackForPaneWithId) => match protobuf_plugin_command.payload
            {
                Some(Payload::EditScrollbackForPaneWithIdPayload(
                    edit_scrollback_for_pane_with_id_payload,
                )) => match edit_scrollback_for_pane_with_id_payload.pane_id {
                    Some(pane_id) => Ok(PluginCommand::EditScrollbackForPaneWithId(
                        pane_id.try_into()?,
                    )),
                    _ => Err("Malformed edit_scrollback_for_pane_with_id payload"),
                },
                _ => Err("Mismatched payload for EditScrollback"),
            },
            Some(CommandName::GetPaneScrollback) => match protobuf_plugin_command.payload {
                Some(Payload::GetPaneScrollbackPayload(get_pane_scrollback_payload)) => {
                    match get_pane_scrollback_payload.pane_id {
                        Some(pane_id) => Ok(PluginCommand::GetPaneScrollback {
                            pane_id: pane_id.try_into()?,
                            get_full_scrollback: get_pane_scrollback_payload.get_full_scrollback,
                        }),
                        _ => Err("Malformed get_pane_scrollback_payload"),
                    }
                },
                _ => Err("Mismatched payload for GetPaneScrollback"),
            },
            Some(CommandName::WriteToPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::WriteToPaneIdPayload(write_to_pane_id_payload)) => {
                    match write_to_pane_id_payload.pane_id {
                        Some(pane_id) => Ok(PluginCommand::WriteToPaneId(
                            write_to_pane_id_payload.bytes_to_write,
                            pane_id.try_into()?,
                        )),
                        _ => Err("Malformed write_to_pane_id payload"),
                    }
                },
                _ => Err("Mismatched payload for WriteToPaneId"),
            },
            Some(CommandName::WriteCharsToPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::WriteCharsToPaneIdPayload(write_chars_to_pane_id_payload)) => {
                    match write_chars_to_pane_id_payload.pane_id {
                        Some(pane_id) => Ok(PluginCommand::WriteCharsToPaneId(
                            write_chars_to_pane_id_payload.chars_to_write,
                            pane_id.try_into()?,
                        )),
                        _ => Err("Malformed write_chars_to_pane_id payload"),
                    }
                },
                _ => Err("Mismatched payload for WriteCharsCharsToPaneId"),
            },
            Some(CommandName::SendSigintToPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::SendSigintToPaneIdPayload(pane_id)) => {
                    Ok(PluginCommand::SendSigintToPaneId(pane_id.try_into()?))
                },
                _ => Err("Mismatched payload for SendSigintToPaneId"),
            },
            Some(CommandName::SendSigkillToPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::SendSigkillToPaneIdPayload(pane_id)) => {
                    Ok(PluginCommand::SendSigkillToPaneId(pane_id.try_into()?))
                },
                _ => Err("Mismatched payload for SendSigkillToPaneId"),
            },
            Some(CommandName::GetPanePid) => match protobuf_plugin_command.payload {
                Some(Payload::GetPanePidPayload(get_pane_pid_payload)) => {
                    match get_pane_pid_payload.pane_id {
                        Some(pane_id) => Ok(PluginCommand::GetPanePid {
                            pane_id: pane_id.try_into()?,
                        }),
                        _ => Err("Malformed get_pane_pid payload"),
                    }
                },
                _ => Err("Mismatched payload for GetPanePid"),
            },
            Some(CommandName::GetPaneRunningCommand) => match protobuf_plugin_command.payload {
                Some(Payload::GetPaneRunningCommandPayload(payload)) => {
                    Ok(PluginCommand::GetPaneRunningCommand {
                        pane_id: payload
                            .pane_id
                            .ok_or("Malformed GetPaneRunningCommand: missing pane_id")?
                            .try_into()?,
                    })
                },
                _ => Err("Malformed GetPaneRunningCommand payload"),
            },
            Some(CommandName::GetPaneCwd) => match protobuf_plugin_command.payload {
                Some(Payload::GetPaneCwdPayload(payload)) => Ok(PluginCommand::GetPaneCwd {
                    pane_id: payload
                        .pane_id
                        .ok_or("Malformed GetPaneCwd: missing pane_id")?
                        .try_into()?,
                }),
                _ => Err("Malformed GetPaneCwd payload"),
            },
            Some(CommandName::OverrideLayout) => match protobuf_plugin_command.payload {
                Some(Payload::OverrideLayoutPayload(override_layout_payload)) => {
                    let layout_info = override_layout_payload
                        .layout_info
                        .ok_or("OverrideLayout missing layout_info")?
                        .try_into()
                        .map_err(|_| "Failed to parse LayoutInfo")?;
                    let context = override_layout_payload
                        .context
                        .into_iter()
                        .map(|c| (c.name, c.value))
                        .collect();
                    Ok(PluginCommand::OverrideLayout(
                        layout_info,
                        override_layout_payload.retain_existing_terminal_panes,
                        override_layout_payload.retain_existing_plugin_panes,
                        override_layout_payload.apply_only_to_active_tab,
                        context,
                    ))
                },
                _ => Err("Mismatched payload for OverrideLayout"),
            },
            Some(CommandName::SaveLayout) => match protobuf_plugin_command.payload {
                Some(Payload::SaveLayoutPayload(save_layout_payload)) => {
                    Ok(PluginCommand::SaveLayout {
                        layout_name: save_layout_payload.layout_name,
                        layout_kdl: save_layout_payload.layout_kdl,
                        overwrite: save_layout_payload.overwrite,
                    })
                },
                _ => Err("Mismatched payload for SaveLayout"),
            },
            Some(CommandName::DeleteLayout) => match protobuf_plugin_command.payload {
                Some(Payload::DeleteLayoutPayload(delete_layout_payload)) => {
                    Ok(PluginCommand::DeleteLayout {
                        layout_name: delete_layout_payload.layout_name,
                    })
                },
                _ => Err("Mismatched payload for DeleteLayout"),
            },
            Some(CommandName::RenameLayout) => match protobuf_plugin_command.payload {
                Some(Payload::RenameLayoutPayload(rename_layout_payload)) => {
                    Ok(PluginCommand::RenameLayout {
                        old_layout_name: rename_layout_payload.old_layout_name,
                        new_layout_name: rename_layout_payload.new_layout_name,
                    })
                },
                _ => Err("Mismatched payload for RenameLayout"),
            },
            Some(CommandName::EditLayout) => match protobuf_plugin_command.payload {
                Some(Payload::EditLayoutPayload(edit_layout_payload)) => {
                    let layout_name = edit_layout_payload.layout_name;
                    let context: BTreeMap<String, String> = edit_layout_payload
                        .context
                        .into_iter()
                        .map(|e| (e.name, e.value))
                        .collect();
                    Ok(PluginCommand::EditLayout {
                        layout_name,
                        context,
                    })
                },
                _ => Err("Mismatched payload for EditLayout"),
            },
            Some(CommandName::MovePaneWithPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::MovePaneWithPaneIdPayload(move_pane_with_pane_id_payload)) => {
                    match move_pane_with_pane_id_payload.pane_id {
                        Some(pane_id) => Ok(PluginCommand::MovePaneWithPaneId(pane_id.try_into()?)),
                        _ => Err("Malformed move_pane_with_pane_id payload"),
                    }
                },
                _ => Err("Mismatched payload for MovePaneWithPaneId"),
            },
            Some(CommandName::MovePaneWithPaneIdInDirection) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::MovePaneWithPaneIdInDirectionPayload(move_payload)) => {
                        match (move_payload.direction, move_payload.pane_id) {
                            (Some(direction), Some(pane_id)) => {
                                Ok(PluginCommand::MovePaneWithPaneIdInDirection(
                                    pane_id.try_into()?,
                                    direction.try_into()?,
                                ))
                            },
                            _ => Err("Malformed MovePaneWithPaneIdInDirection payload"),
                        }
                    },
                    _ => Err("Mismatched payload for MovePaneWithDirection"),
                }
            },
            Some(CommandName::ClearScreenForPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::ClearScreenForPaneIdPayload(clear_screen_for_pane_id_payload)) => {
                    match clear_screen_for_pane_id_payload.pane_id {
                        Some(pane_id) => {
                            Ok(PluginCommand::ClearScreenForPaneId(pane_id.try_into()?))
                        },
                        _ => Err("Malformed clear_screen_for_pane_id_payload payload"),
                    }
                },
                _ => Err("Mismatched payload for ClearScreenForPaneId"),
            },
            Some(CommandName::ScrollUpInPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::ScrollUpInPaneIdPayload(scroll_up_in_pane_id_payload)) => {
                    match scroll_up_in_pane_id_payload.pane_id {
                        Some(pane_id) => Ok(PluginCommand::ScrollUpInPaneId(pane_id.try_into()?)),
                        _ => Err("Malformed scroll_up_in_pane_id_payload payload"),
                    }
                },
                _ => Err("Mismatched payload for ScrollUpInPaneId"),
            },
            Some(CommandName::ScrollDownInPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::ScrollDownInPaneIdPayload(scroll_down_in_pane_id_payload)) => {
                    match scroll_down_in_pane_id_payload.pane_id {
                        Some(pane_id) => Ok(PluginCommand::ScrollDownInPaneId(pane_id.try_into()?)),
                        _ => Err("Malformed scroll_down_in_pane_id_payload payload"),
                    }
                },
                _ => Err("Mismatched payload for ScrollDownInPaneId"),
            },
            Some(CommandName::ScrollToTopInPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::ScrollToTopInPaneIdPayload(scroll_to_top_in_pane_id_payload)) => {
                    match scroll_to_top_in_pane_id_payload.pane_id {
                        Some(pane_id) => {
                            Ok(PluginCommand::ScrollToTopInPaneId(pane_id.try_into()?))
                        },
                        _ => Err("Malformed scroll_to_top_in_pane_id_payload payload"),
                    }
                },
                _ => Err("Mismatched payload for ScrollToTopInPaneId"),
            },
            Some(CommandName::ScrollToBottomInPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::ScrollToBottomInPaneIdPayload(
                    scroll_to_bottom_in_pane_id_payload,
                )) => match scroll_to_bottom_in_pane_id_payload.pane_id {
                    Some(pane_id) => Ok(PluginCommand::ScrollToBottomInPaneId(pane_id.try_into()?)),
                    _ => Err("Malformed scroll_to_bottom_in_pane_id_payload payload"),
                },
                _ => Err("Mismatched payload for ScrollToBottomInPaneId"),
            },
            Some(CommandName::PageScrollUpInPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::PageScrollUpInPaneIdPayload(page_scroll_up_in_pane_id_payload)) => {
                    match page_scroll_up_in_pane_id_payload.pane_id {
                        Some(pane_id) => {
                            Ok(PluginCommand::PageScrollUpInPaneId(pane_id.try_into()?))
                        },
                        _ => Err("Malformed page_scroll_up_in_pane_id_payload payload"),
                    }
                },
                _ => Err("Mismatched payload for PageScrollUpInPaneId"),
            },
            Some(CommandName::PageScrollDownInPaneId) => match protobuf_plugin_command.payload {
                Some(Payload::PageScrollDownInPaneIdPayload(
                    page_scroll_down_in_pane_id_payload,
                )) => match page_scroll_down_in_pane_id_payload.pane_id {
                    Some(pane_id) => Ok(PluginCommand::PageScrollDownInPaneId(pane_id.try_into()?)),
                    _ => Err("Malformed page_scroll_down_in_pane_id_payload payload"),
                },
                _ => Err("Mismatched payload for PageScrollDownInPaneId"),
            },
            Some(CommandName::TogglePaneIdFullscreen) => match protobuf_plugin_command.payload {
                Some(Payload::TogglePaneIdFullscreenPayload(toggle_pane_id_fullscreen_payload)) => {
                    match toggle_pane_id_fullscreen_payload.pane_id {
                        Some(pane_id) => {
                            Ok(PluginCommand::TogglePaneIdFullscreen(pane_id.try_into()?))
                        },
                        _ => Err("Malformed toggle_pane_id_fullscreen_payload payload"),
                    }
                },
                _ => Err("Mismatched payload for TogglePaneIdFullscreen"),
            },
            Some(CommandName::TogglePaneEmbedOrEjectForPaneId) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::TogglePaneEmbedOrEjectForPaneIdPayload(
                        toggle_pane_embed_or_eject_payload,
                    )) => match toggle_pane_embed_or_eject_payload.pane_id {
                        Some(pane_id) => Ok(PluginCommand::TogglePaneEmbedOrEjectForPaneId(
                            pane_id.try_into()?,
                        )),
                        _ => Err("Malformed toggle_pane_embed_or_eject_payload payload"),
                    },
                    _ => Err("Mismatched payload for TogglePaneEmbedOrEjectForPaneId"),
                }
            },
            Some(CommandName::CloseTabWithIndex) => match protobuf_plugin_command.payload {
                Some(Payload::CloseTabWithIndexPayload(close_tab_index_payload)) => Ok(
                    PluginCommand::CloseTabWithIndex(close_tab_index_payload.tab_index as usize),
                ),
                _ => Err("Mismatched payload for CloseTabWithIndex"),
            },
            Some(CommandName::BreakPanesToNewTab) => match protobuf_plugin_command.payload {
                Some(Payload::BreakPanesToNewTabPayload(break_panes_to_new_tab_payload)) => {
                    Ok(PluginCommand::BreakPanesToNewTab(
                        break_panes_to_new_tab_payload
                            .pane_ids
                            .into_iter()
                            .filter_map(|p_id| p_id.try_into().ok())
                            .collect(),
                        break_panes_to_new_tab_payload.new_tab_name,
                        break_panes_to_new_tab_payload.should_change_focus_to_new_tab,
                    ))
                },
                _ => Err("Mismatched payload for BreakPanesToNewTab"),
            },
            Some(CommandName::BreakPanesToTabWithIndex) => match protobuf_plugin_command.payload {
                Some(Payload::BreakPanesToTabWithIndexPayload(
                    break_panes_to_tab_with_index_payload,
                )) => Ok(PluginCommand::BreakPanesToTabWithIndex(
                    break_panes_to_tab_with_index_payload
                        .pane_ids
                        .into_iter()
                        .filter_map(|p_id| p_id.try_into().ok())
                        .collect(),
                    break_panes_to_tab_with_index_payload.tab_index as usize,
                    break_panes_to_tab_with_index_payload.should_change_focus_to_target_tab,
                )),
                _ => Err("Mismatched payload for BreakPanesToTabWithIndex"),
            },
            Some(CommandName::SwitchTabToId) => match protobuf_plugin_command.payload {
                Some(Payload::SwitchTabToIdPayload(payload)) => {
                    Ok(PluginCommand::SwitchTabToId(payload.tab_id))
                },
                _ => Err("Mismatched payload for SwitchTabToId"),
            },
            Some(CommandName::GoToTabWithId) => match protobuf_plugin_command.payload {
                Some(Payload::GoToTabWithIdPayload(payload)) => {
                    Ok(PluginCommand::GoToTabWithId(payload.tab_id))
                },
                _ => Err("Mismatched payload for GoToTabWithId"),
            },
            Some(CommandName::CloseTabWithId) => match protobuf_plugin_command.payload {
                Some(Payload::CloseTabWithIdPayload(payload)) => {
                    Ok(PluginCommand::CloseTabWithId(payload.tab_id))
                },
                _ => Err("Mismatched payload for CloseTabWithId"),
            },
            Some(CommandName::RenameTabWithId) => match protobuf_plugin_command.payload {
                Some(Payload::RenameTabWithIdPayload(payload)) => Ok(
                    PluginCommand::RenameTabWithId(payload.tab_id, payload.new_name),
                ),
                _ => Err("Mismatched payload for RenameTabWithId"),
            },
            Some(CommandName::BreakPanesToTabWithId) => match protobuf_plugin_command.payload {
                Some(Payload::BreakPanesToTabWithIdPayload(payload)) => {
                    Ok(PluginCommand::BreakPanesToTabWithId(
                        payload
                            .pane_ids
                            .into_iter()
                            .filter_map(|p_id| p_id.try_into().ok())
                            .collect(),
                        payload.tab_id,
                        payload.should_change_focus_to_target_tab,
                    ))
                },
                _ => Err("Mismatched payload for BreakPanesToTabWithId"),
            },
            Some(CommandName::ReloadPlugin) => match protobuf_plugin_command.payload {
                Some(Payload::ReloadPluginPayload(reload_plugin_payload)) => {
                    Ok(PluginCommand::ReloadPlugin(reload_plugin_payload.plugin_id))
                },
                _ => Err("Mismatched payload for ReloadPlugin"),
            },
            Some(CommandName::LoadNewPlugin) => match protobuf_plugin_command.payload {
                Some(Payload::LoadNewPluginPayload(load_new_plugin_payload)) => {
                    Ok(PluginCommand::LoadNewPlugin {
                        url: load_new_plugin_payload.plugin_url,
                        config: load_new_plugin_payload
                            .plugin_config
                            .into_iter()
                            .map(|e| (e.name, e.value))
                            .collect(),
                        load_in_background: load_new_plugin_payload
                            .should_load_plugin_in_background,
                        skip_plugin_cache: load_new_plugin_payload.should_skip_plugin_cache,
                    })
                },
                _ => Err("Mismatched payload for LoadNewPlugin"),
            },
            Some(CommandName::RebindKeys) => match protobuf_plugin_command.payload {
                Some(Payload::RebindKeysPayload(rebind_keys_payload)) => {
                    Ok(PluginCommand::RebindKeys {
                        keys_to_rebind: rebind_keys_payload
                            .keys_to_rebind
                            .into_iter()
                            .filter_map(|k| key_to_rebind_to_plugin_command_assets(k))
                            .collect(),
                        keys_to_unbind: rebind_keys_payload
                            .keys_to_unbind
                            .into_iter()
                            .filter_map(|k| key_to_unbind_to_plugin_command_assets(k))
                            .collect(),
                        write_config_to_disk: rebind_keys_payload.write_config_to_disk,
                    })
                },
                _ => Err("Mismatched payload for RebindKeys"),
            },
            Some(CommandName::ListClients) => match protobuf_plugin_command.payload {
                Some(_) => Err("ListClients should have no payload, found a payload"),
                None => Ok(PluginCommand::ListClients),
            },
            Some(CommandName::ChangeHostFolder) => match protobuf_plugin_command.payload {
                Some(Payload::ChangeHostFolderPayload(change_host_folder_payload)) => {
                    Ok(PluginCommand::ChangeHostFolder(PathBuf::from(
                        change_host_folder_payload.new_host_folder,
                    )))
                },
                _ => Err("Mismatched payload for ChangeHostFolder"),
            },
            Some(CommandName::SetFloatingPanePinned) => match protobuf_plugin_command.payload {
                Some(Payload::SetFloatingPanePinnedPayload(set_floating_pane_pinned_payload)) => {
                    match set_floating_pane_pinned_payload
                        .pane_id
                        .and_then(|p| p.try_into().ok())
                    {
                        Some(pane_id) => Ok(PluginCommand::SetFloatingPanePinned(
                            pane_id,
                            set_floating_pane_pinned_payload.should_be_pinned,
                        )),
                        None => Err("PaneId not found!"),
                    }
                },
                _ => Err("Mismatched payload for SetFloatingPanePinned"),
            },
            Some(CommandName::StackPanes) => match protobuf_plugin_command.payload {
                Some(Payload::StackPanesPayload(stack_panes_payload)) => {
                    Ok(PluginCommand::StackPanes(
                        stack_panes_payload
                            .pane_ids
                            .into_iter()
                            .filter_map(|p_id| p_id.try_into().ok())
                            .collect(),
                    ))
                },
                _ => Err("Mismatched payload for StackPanes"),
            },
            Some(CommandName::ChangeFloatingPanesCoordinates) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::ChangeFloatingPanesCoordinatesPayload(
                        change_floating_panes_coordinates_payload,
                    )) => Ok(PluginCommand::ChangeFloatingPanesCoordinates(
                        change_floating_panes_coordinates_payload
                            .pane_ids_and_floating_panes_coordinates
                            .into_iter()
                            .filter_map(|p_id_a_fp| {
                                let pane_id: PaneId = p_id_a_fp.pane_id?.try_into().ok()?;
                                let floating_pane_coordinates: FloatingPaneCoordinates =
                                    p_id_a_fp.floating_pane_coordinates?.try_into().ok()?;
                                Some((pane_id, floating_pane_coordinates))
                            })
                            .collect(),
                    )),
                    _ => Err("Mismatched payload for ChangeFloatingPanesCoordinates"),
                }
            },
            Some(CommandName::TogglePaneBorderless) => match protobuf_plugin_command.payload {
                Some(Payload::TogglePaneBorderlessPayload(toggle_payload)) => {
                    match toggle_payload.pane_id {
                        Some(pane_id) => {
                            Ok(PluginCommand::TogglePaneBorderless(pane_id.try_into()?))
                        },
                        None => Err("Malformed TogglePaneBorderless payload"),
                    }
                },
                _ => Err("Mismatched payload for TogglePaneBorderless"),
            },
            Some(CommandName::SetPaneBorderless) => match protobuf_plugin_command.payload {
                Some(Payload::SetPaneBorderlessPayload(payload)) => {
                    match (payload.pane_id, payload.borderless) {
                        (Some(pane_id), borderless) => Ok(PluginCommand::SetPaneBorderless(
                            pane_id.try_into()?,
                            borderless,
                        )),
                        _ => Err("Malformed SetPaneBorderless payload"),
                    }
                },
                _ => Err("Mismatched payload for SetPaneBorderless"),
            },
            Some(CommandName::OpenCommandPaneNearPlugin) => match protobuf_plugin_command.payload {
                Some(Payload::OpenCommandPaneNearPluginPayload(command_to_run_payload)) => {
                    match command_to_run_payload.command_to_run {
                        Some(command_to_run) => {
                            let context: BTreeMap<String, String> = command_to_run_payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenCommandPaneNearPlugin(
                                command_to_run.try_into()?,
                                context,
                            ))
                        },
                        None => Err("Malformed open command pane near plugin payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenCommandPaneNearPlugin"),
            },
            Some(CommandName::OpenTerminalNearPlugin) => match protobuf_plugin_command.payload {
                Some(Payload::OpenTerminalNearPluginPayload(open_terminal_near_plugin_payload)) => {
                    match open_terminal_near_plugin_payload.file_to_open {
                        Some(file_to_open) => Ok(PluginCommand::OpenTerminalNearPlugin(
                            file_to_open.try_into()?,
                        )),
                        None => Err("Malformed open terminal near plugin payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenTerminalNearPluginPayload"),
            },
            Some(CommandName::OpenTerminalFloatingNearPlugin) => match protobuf_plugin_command
                .payload
            {
                Some(Payload::OpenTerminalFloatingNearPluginPayload(
                    open_terminal_floating_near_plugin_payload,
                )) => {
                    let floating_pane_coordinates = open_terminal_floating_near_plugin_payload
                        .floating_pane_coordinates
                        .map(|f| f.into());
                    match open_terminal_floating_near_plugin_payload.file_to_open {
                        Some(file_to_open) => Ok(PluginCommand::OpenTerminalFloatingNearPlugin(
                            file_to_open.try_into()?,
                            floating_pane_coordinates,
                        )),
                        None => Err("Malformed open terminal floating near plugin payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenTerminalFloatingNearPlugin"),
            },
            Some(CommandName::OpenTerminalInPlaceOfPlugin) => match protobuf_plugin_command.payload
            {
                Some(Payload::OpenTerminalInPlaceOfPluginPayload(
                    open_terminal_in_place_of_plugin_payload,
                )) => match open_terminal_in_place_of_plugin_payload.file_to_open {
                    Some(file_to_open) => Ok(PluginCommand::OpenTerminalInPlaceOfPlugin(
                        file_to_open.try_into()?,
                        open_terminal_in_place_of_plugin_payload.close_plugin_after_replace,
                    )),
                    None => Err("Malformed open terminal in place of plugin payload"),
                },
                _ => Err("Mismatched payload for OpenTerminalInPlaceOfPlugin"),
            },
            Some(CommandName::OpenCommandPaneFloatingNearPlugin) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::OpenCommandPaneFloatingNearPluginPayload(
                        open_command_pane_floating_near_plugin,
                    )) => match open_command_pane_floating_near_plugin.command_to_run {
                        Some(command_to_run) => {
                            let context: BTreeMap<String, String> =
                                open_command_pane_floating_near_plugin
                                    .context
                                    .into_iter()
                                    .map(|e| (e.name, e.value))
                                    .collect();
                            let floating_pane_coordinates = open_command_pane_floating_near_plugin
                                .floating_pane_coordinates
                                .map(|f| f.into());
                            Ok(PluginCommand::OpenCommandPaneFloatingNearPlugin(
                                command_to_run.try_into()?,
                                floating_pane_coordinates,
                                context,
                            ))
                        },
                        None => Err("Malformed open command pane floating near plugin payload"),
                    },
                    _ => Err("Mismatched payload for OpenCommandPaneFloatingNearPlugin"),
                }
            },
            Some(CommandName::OpenCommandPaneInPlaceOfPlugin) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::OpenCommandPaneInPlaceOfPluginPayload(
                        open_command_pane_in_place_of_plugin_payload,
                    )) => match open_command_pane_in_place_of_plugin_payload.command_to_run {
                        Some(command_to_run) => {
                            let context: BTreeMap<String, String> =
                                open_command_pane_in_place_of_plugin_payload
                                    .context
                                    .into_iter()
                                    .map(|e| (e.name, e.value))
                                    .collect();
                            Ok(PluginCommand::OpenCommandPaneInPlaceOfPlugin(
                                command_to_run.try_into()?,
                                open_command_pane_in_place_of_plugin_payload
                                    .close_plugin_after_replace,
                                context,
                            ))
                        },
                        None => Err("Malformed open command pane in place of plugin payload"),
                    },
                    _ => Err("Mismatched payload for OpenCommandPaneInPlaceOfPlugin"),
                }
            },
            Some(CommandName::OpenFileNearPlugin) => match protobuf_plugin_command.payload {
                Some(Payload::OpenFileNearPluginPayload(file_to_open_payload)) => {
                    match file_to_open_payload.file_to_open {
                        Some(file_to_open) => {
                            let context: BTreeMap<String, String> = file_to_open_payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenFileNearPlugin(
                                file_to_open.try_into()?,
                                context,
                            ))
                        },
                        None => Err("Malformed open file payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenFileNearPlugin"),
            },
            Some(CommandName::OpenFileFloatingNearPlugin) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::OpenFileFloatingNearPluginPayload(file_to_open_payload)) => {
                        let floating_pane_coordinates = file_to_open_payload
                            .floating_pane_coordinates
                            .map(|f| f.into());
                        let context: BTreeMap<String, String> = file_to_open_payload
                            .context
                            .into_iter()
                            .map(|e| (e.name, e.value))
                            .collect();
                        match file_to_open_payload.file_to_open {
                            Some(file_to_open) => Ok(PluginCommand::OpenFileFloatingNearPlugin(
                                file_to_open.try_into()?,
                                floating_pane_coordinates,
                                context,
                            )),
                            None => Err("Malformed open file payload"),
                        }
                    },
                    _ => Err("Mismatched payload for OpenFileFloatingNearPlugin"),
                }
            },
            Some(CommandName::OpenFileInPlaceOfPlugin) => match protobuf_plugin_command.payload {
                Some(Payload::OpenFileInPlaceOfPluginPayload(file_to_open_payload)) => {
                    match file_to_open_payload.file_to_open {
                        Some(file_to_open) => {
                            let context: BTreeMap<String, String> = file_to_open_payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenFileInPlaceOfPlugin(
                                file_to_open.try_into()?,
                                file_to_open_payload.close_plugin_after_replace,
                                context,
                            ))
                        },
                        None => Err("Malformed open file in place payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenFileInPlaceOfPlugin"),
            },
            Some(CommandName::StartWebServer) => {
                if protobuf_plugin_command.payload.is_some() {
                    Err("StartWebServer should not have a payload")
                } else {
                    Ok(PluginCommand::StartWebServer)
                }
            },
            Some(CommandName::StopWebServer) => {
                if protobuf_plugin_command.payload.is_some() {
                    Err("StopWebServer should not have a payload")
                } else {
                    Ok(PluginCommand::StopWebServer)
                }
            },
            Some(CommandName::QueryWebServerStatus) => {
                if protobuf_plugin_command.payload.is_some() {
                    Err("QueryWebServerStatus should not have a payload")
                } else {
                    Ok(PluginCommand::QueryWebServerStatus)
                }
            },
            Some(CommandName::GroupAndUngroupPanes) => match protobuf_plugin_command.payload {
                Some(Payload::GroupAndUngroupPanesPayload(group_and_ungroup_panes_payload)) => {
                    Ok(PluginCommand::GroupAndUngroupPanes(
                        group_and_ungroup_panes_payload
                            .pane_ids_to_group
                            .into_iter()
                            .filter_map(|p| p.try_into().ok())
                            .collect(),
                        group_and_ungroup_panes_payload
                            .pane_ids_to_ungroup
                            .into_iter()
                            .filter_map(|p| p.try_into().ok())
                            .collect(),
                        group_and_ungroup_panes_payload.for_all_clients,
                    ))
                },
                _ => Err("Mismatched payload for GroupAndUngroupPanes"),
            },
            Some(CommandName::HighlightAndUnhighlightPanes) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::HighlightAndUnhighlightPanesPayload(
                        highlight_and_unhighlight_panes_payload,
                    )) => Ok(PluginCommand::HighlightAndUnhighlightPanes(
                        highlight_and_unhighlight_panes_payload
                            .pane_ids_to_highlight
                            .into_iter()
                            .filter_map(|p| p.try_into().ok())
                            .collect(),
                        highlight_and_unhighlight_panes_payload
                            .pane_ids_to_unhighlight
                            .into_iter()
                            .filter_map(|p| p.try_into().ok())
                            .collect(),
                    )),
                    _ => Err("Mismatched payload for HighlightAndUnhighlightPanes"),
                }
            },
            Some(CommandName::CloseMultiplePanes) => match protobuf_plugin_command.payload {
                Some(Payload::CloseMultiplePanesPayload(close_multiple_panes_payload)) => {
                    Ok(PluginCommand::CloseMultiplePanes(
                        close_multiple_panes_payload
                            .pane_ids
                            .into_iter()
                            .filter_map(|p| p.try_into().ok())
                            .collect(),
                    ))
                },
                _ => Err("Mismatched payload for CloseMultiplePanes"),
            },
            Some(CommandName::FloatMultiplePanes) => match protobuf_plugin_command.payload {
                Some(Payload::FloatMultiplePanesPayload(float_multiple_panes_payload)) => {
                    Ok(PluginCommand::FloatMultiplePanes(
                        float_multiple_panes_payload
                            .pane_ids
                            .into_iter()
                            .filter_map(|p| p.try_into().ok())
                            .collect(),
                    ))
                },
                _ => Err("Mismatched payload for FloatMultiplePanes"),
            },
            Some(CommandName::EmbedMultiplePanes) => match protobuf_plugin_command.payload {
                Some(Payload::EmbedMultiplePanesPayload(embed_multiple_panes_payload)) => {
                    Ok(PluginCommand::EmbedMultiplePanes(
                        embed_multiple_panes_payload
                            .pane_ids
                            .into_iter()
                            .filter_map(|p| p.try_into().ok())
                            .collect(),
                    ))
                },
                _ => Err("Mismatched payload for EmbedMultiplePanes"),
            },
            Some(CommandName::ShareCurrentSession) => {
                if protobuf_plugin_command.payload.is_some() {
                    Err("ShareCurrentSession should not have a payload")
                } else {
                    Ok(PluginCommand::ShareCurrentSession)
                }
            },
            Some(CommandName::StopSharingCurrentSession) => {
                if protobuf_plugin_command.payload.is_some() {
                    Err("StopSharingCurrentSession should not have a payload")
                } else {
                    Ok(PluginCommand::StopSharingCurrentSession)
                }
            },
            Some(CommandName::SetSelfMouseSelectionSupport) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::SetSelfMouseSelectionSupportPayload(
                        set_self_mouse_selection_support_payload,
                    )) => Ok(PluginCommand::SetSelfMouseSelectionSupport(
                        set_self_mouse_selection_support_payload.support_mouse_selection,
                    )),
                    _ => Err("SetSelfMouseSelectionSupport requires a payload"),
                }
            },
            Some(CommandName::GenerateWebLoginToken) => match protobuf_plugin_command.payload {
                Some(Payload::GenerateWebLoginTokenPayload(generate_web_login_token_payload)) => {
                    Ok(PluginCommand::GenerateWebLoginToken(
                        generate_web_login_token_payload.token_label,
                        generate_web_login_token_payload.read_only.unwrap_or(false),
                    ))
                },
                _ => Err("GenerateWebLoginToken requires a payload"),
            },
            Some(CommandName::RevokeWebLoginToken) => match protobuf_plugin_command.payload {
                Some(Payload::RevokeWebLoginTokenPayload(revoke_web_login_token_payload)) => Ok(
                    PluginCommand::RevokeWebLoginToken(revoke_web_login_token_payload.token_label),
                ),
                _ => Err("RevokeWebLoginToken requires a payload"),
            },
            Some(CommandName::ListWebLoginTokens) => {
                if protobuf_plugin_command.payload.is_some() {
                    Err("ListWebLoginTokens should not have a payload")
                } else {
                    Ok(PluginCommand::ListWebLoginTokens)
                }
            },
            Some(CommandName::RevokeAllWebLoginTokens) => {
                if protobuf_plugin_command.payload.is_some() {
                    Err("RevokeAllWebLoginTokens should not have a payload")
                } else {
                    Ok(PluginCommand::RevokeAllWebLoginTokens)
                }
            },
            Some(CommandName::RenameWebLoginToken) => match protobuf_plugin_command.payload {
                Some(Payload::RenameWebLoginTokenPayload(rename_web_login_token_payload)) => {
                    Ok(PluginCommand::RenameWebLoginToken(
                        rename_web_login_token_payload.old_name,
                        rename_web_login_token_payload.new_name,
                    ))
                },
                _ => Err("RenameWebLoginToken requires a payload"),
            },
            Some(CommandName::InterceptKeyPresses) => match protobuf_plugin_command.payload {
                Some(_) => Err("InterceptKeyPresses should have no payload, found a payload"),
                None => Ok(PluginCommand::InterceptKeyPresses),
            },
            Some(CommandName::ClearKeyPressesIntercepts) => match protobuf_plugin_command.payload {
                Some(_) => Err("ClearKeyPressesIntercepts should have no payload, found a payload"),
                None => Ok(PluginCommand::ClearKeyPressesIntercepts),
            },
            Some(CommandName::ReplacePaneWithExistingPane) => match protobuf_plugin_command.payload
            {
                Some(Payload::ReplacePaneWithExistingPanePayload(
                    replace_pane_with_other_pane_payload,
                )) => Ok(PluginCommand::ReplacePaneWithExistingPane(
                    replace_pane_with_other_pane_payload
                        .pane_id_to_replace
                        .and_then(|p_id| PaneId::try_from(p_id).ok())
                        .ok_or("Failed to parse ReplacePaneWithExistingPanePayload")?,
                    replace_pane_with_other_pane_payload
                        .existing_pane_id
                        .and_then(|p_id| PaneId::try_from(p_id).ok())
                        .ok_or("Failed to parse ReplacePaneWithExistingPanePayload")?,
                    replace_pane_with_other_pane_payload.suppress_replaced_pane,
                )),
                _ => Err("Mismatched payload for ReplacePaneWithExistingPane"),
            },
            Some(CommandName::RunAction) => match protobuf_plugin_command.payload {
                Some(Payload::RunActionPayload(protobuf_payload)) => {
                    let action = Action::try_from(
                        protobuf_payload
                            .action
                            .ok_or("Missing action in RunAction payload")?,
                    )
                    .map_err(|_| "Failed to convert protobuf action")?;

                    let context: BTreeMap<String, String> = protobuf_payload
                        .context
                        .into_iter()
                        .map(|item| (item.name, item.value))
                        .collect();

                    Ok(PluginCommand::RunAction(action, context))
                },
                _ => Err("Mismatched payload for RunAction"),
            },
            Some(CommandName::CopyToClipboard) => match protobuf_plugin_command.payload {
                Some(Payload::CopyToClipboardPayload(payload)) => {
                    Ok(PluginCommand::CopyToClipboard(payload.text))
                },
                _ => Err("Mismatched payload for CopyToClipboard"),
            },
            Some(CommandName::GenerateRandomName) => Ok(PluginCommand::GenerateRandomName),
            Some(CommandName::DumpLayout) => match protobuf_plugin_command.payload {
                Some(Payload::DumpLayoutPayload(payload)) => {
                    Ok(PluginCommand::DumpLayout(payload.layout_name))
                },
                _ => Err("Mismatched payload for DumpLayout"),
            },
            Some(CommandName::ParseLayout) => match protobuf_plugin_command.payload {
                Some(Payload::ParseLayoutPayload(payload)) => {
                    Ok(PluginCommand::ParseLayout(payload.layout_string))
                },
                _ => Err("Mismatched payload for ParseLayout"),
            },
            Some(CommandName::GetLayoutDir) => Ok(PluginCommand::GetLayoutDir),
            Some(CommandName::GetFocusedPaneInfo) => Ok(PluginCommand::GetFocusedPaneInfo),
            Some(CommandName::SaveSession) => Ok(PluginCommand::SaveSession),
            Some(CommandName::CurrentSessionLastSavedTime) => {
                Ok(PluginCommand::CurrentSessionLastSavedTime)
            },
            Some(CommandName::GetPaneInfo) => match protobuf_plugin_command.payload {
                Some(Payload::GetPaneInfoPayload(get_pane_info_payload)) => {
                    let pane_id = get_pane_info_payload
                        .pane_id
                        .ok_or("Malformed pane_id for GetPaneInfo")
                        .and_then(|p| p.try_into())?;
                    Ok(PluginCommand::GetPaneInfo(pane_id))
                },
                _ => Err("Malformed payload for GetPaneInfo"),
            },
            Some(CommandName::GetTabInfo) => match protobuf_plugin_command.payload {
                Some(Payload::GetTabInfoPayload(get_tab_info_payload)) => {
                    let tab_id = get_tab_info_payload.tab_id as usize;
                    Ok(PluginCommand::GetTabInfo(tab_id))
                },
                _ => Err("Malformed payload for GetTabInfo"),
            },
            Some(CommandName::GetSessionEnvironmentVariables) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::GetSessionEnvironmentVariablesPayload(_)) => {
                        Ok(PluginCommand::GetSessionEnvironmentVariables)
                    },
                    _ => Err("Mismatched payload for GetSessionEnvironmentVariables"),
                }
            },
            Some(CommandName::OpenCommandPaneInNewTab) => match protobuf_plugin_command.payload {
                Some(Payload::OpenCommandPaneInNewTabPayload(payload)) => {
                    match payload.command_to_run {
                        Some(command_to_run) => {
                            let context: BTreeMap<String, String> = payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenCommandPaneInNewTab(
                                command_to_run.try_into()?,
                                context,
                            ))
                        },
                        None => Err("Malformed open_command_pane_in_new_tab payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenCommandPaneInNewTab"),
            },
            Some(CommandName::OpenPluginPaneInNewTab) => match protobuf_plugin_command.payload {
                Some(Payload::OpenPluginPaneInNewTabPayload(payload)) => {
                    let configuration: BTreeMap<String, String> =
                        payload.configuration.into_iter().collect();
                    let context: BTreeMap<String, String> = payload
                        .context
                        .into_iter()
                        .map(|e| (e.name, e.value))
                        .collect();
                    Ok(PluginCommand::OpenPluginPaneInNewTab {
                        plugin_url: payload.plugin_url,
                        configuration,
                        context,
                    })
                },
                _ => Err("Mismatched payload for OpenPluginPaneInNewTab"),
            },
            Some(CommandName::OpenEditorPaneInNewTab) => match protobuf_plugin_command.payload {
                Some(Payload::OpenEditorPaneInNewTabPayload(payload)) => {
                    match payload.file_to_open {
                        Some(file) => {
                            let context: BTreeMap<String, String> = payload
                                .context
                                .into_iter()
                                .map(|e| (e.name, e.value))
                                .collect();
                            Ok(PluginCommand::OpenEditorPaneInNewTab(
                                file.try_into()?,
                                context,
                            ))
                        },
                        None => Err("Malformed open_editor_pane_in_new_tab payload"),
                    }
                },
                _ => Err("Mismatched payload for OpenEditorPaneInNewTab"),
            },
            Some(CommandName::OpenCommandPaneInPlaceOfPaneId) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::OpenCommandPaneInPlaceOfPaneIdPayload(payload)) => {
                        match (payload.pane_id_to_replace, payload.command_to_run) {
                            (Some(pane_id), Some(command_to_run)) => {
                                let context: BTreeMap<String, String> = payload
                                    .context
                                    .into_iter()
                                    .map(|e| (e.name, e.value))
                                    .collect();
                                Ok(PluginCommand::OpenCommandPaneInPlaceOfPaneId(
                                    pane_id.try_into()?,
                                    command_to_run.try_into()?,
                                    payload.close_replaced_pane,
                                    context,
                                ))
                            },
                            _ => Err("Malformed open_command_pane_in_place_of_pane_id payload"),
                        }
                    },
                    _ => Err("Mismatched payload for OpenCommandPaneInPlaceOfPaneId"),
                }
            },
            Some(CommandName::OpenTerminalPaneInPlaceOfPaneId) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::OpenTerminalPaneInPlaceOfPaneIdPayload(payload)) => {
                        match payload.pane_id_to_replace {
                            Some(pane_id) => {
                                let cwd = payload.cwd.map(|f| f.try_into()).transpose()?;
                                let cwd = cwd.unwrap_or_else(|| crate::data::FileToOpen {
                                    path: std::path::PathBuf::from("."),
                                    ..Default::default()
                                });
                                Ok(PluginCommand::OpenTerminalPaneInPlaceOfPaneId(
                                    pane_id.try_into()?,
                                    cwd,
                                    payload.close_replaced_pane,
                                ))
                            },
                            None => Err("Malformed open_terminal_pane_in_place_of_pane_id payload"),
                        }
                    },
                    _ => Err("Mismatched payload for OpenTerminalPaneInPlaceOfPaneId"),
                }
            },
            Some(CommandName::OpenEditPaneInPlaceOfPaneId) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::OpenEditPaneInPlaceOfPaneIdPayload(payload)) => {
                        match (payload.pane_id_to_replace, payload.file_to_open) {
                            (Some(pane_id), Some(file_to_open)) => {
                                let context: BTreeMap<String, String> = payload
                                    .context
                                    .into_iter()
                                    .map(|e| (e.name, e.value))
                                    .collect();
                                Ok(PluginCommand::OpenEditPaneInPlaceOfPaneId(
                                    pane_id.try_into()?,
                                    file_to_open.try_into()?,
                                    payload.close_replaced_pane,
                                    context,
                                ))
                            },
                            _ => Err("Malformed open_edit_pane_in_place_of_pane_id payload"),
                        }
                    },
                    _ => Err("Mismatched payload for OpenEditPaneInPlaceOfPaneId"),
                }
            },
            Some(CommandName::HideFloatingPanes) => match protobuf_plugin_command.payload {
                Some(Payload::HideFloatingPanesPayload(payload)) => {
                    Ok(PluginCommand::HideFloatingPanes {
                        tab_id: payload.tab_id.map(|id| id as usize),
                    })
                },
                None => Ok(PluginCommand::HideFloatingPanes { tab_id: None }),
                _ => Err("Mismatched payload for HideFloatingPanes"),
            },
            Some(CommandName::ShowFloatingPanes) => match protobuf_plugin_command.payload {
                Some(Payload::ShowFloatingPanesPayload(payload)) => {
                    Ok(PluginCommand::ShowFloatingPanes {
                        tab_id: payload.tab_id.map(|id| id as usize),
                    })
                },
                None => Ok(PluginCommand::ShowFloatingPanes { tab_id: None }),
                _ => Err("Mismatched payload for ShowFloatingPanes"),
            },
            Some(CommandName::SetPaneColor) => match protobuf_plugin_command.payload {
                Some(Payload::SetPaneColorPayload(payload)) => match payload.pane_id {
                    Some(pane_id) => Ok(PluginCommand::SetPaneColor(
                        pane_id.try_into()?,
                        payload.fg,
                        payload.bg,
                    )),
                    None => Err("Malformed SetPaneColor payload"),
                },
                _ => Err("Mismatched payload for SetPaneColor"),
            },
            Some(CommandName::SetPaneRegexHighlights) => {
                match protobuf_plugin_command.payload {
                    Some(Payload::SetPaneRegexHighlightsPayload(p)) => {
                        let pane_id: PaneId = p
                            .pane_id
                            .ok_or("Missing pane_id in SetPaneRegexHighlights")?
                            .try_into()?;
                        let highlights = p
                            .highlights
                            .into_iter()
                            .map(|h| {
                                let style = match h.style.and_then(|s| s.style) {
                                    Some(ProtobufHighlightStyleVariant::Emphasis0(_)) => {
                                        HighlightStyle::Emphasis0
                                    },
                                    Some(ProtobufHighlightStyleVariant::Emphasis1(_)) => {
                                        HighlightStyle::Emphasis1
                                    },
                                    Some(ProtobufHighlightStyleVariant::Emphasis2(_)) => {
                                        HighlightStyle::Emphasis2
                                    },
                                    Some(ProtobufHighlightStyleVariant::Emphasis3(_)) => {
                                        HighlightStyle::Emphasis3
                                    },
                                    Some(ProtobufHighlightStyleVariant::CustomRgb(c)) => {
                                        HighlightStyle::CustomRgb {
                                            fg: match (c.fg_r, c.fg_g, c.fg_b) {
                                                (Some(r), Some(g), Some(b)) => {
                                                    Some((r as u8, g as u8, b as u8))
                                                },
                                                _ => None,
                                            },
                                            bg: match (c.bg_r, c.bg_g, c.bg_b) {
                                                (Some(r), Some(g), Some(b)) => {
                                                    Some((r as u8, g as u8, b as u8))
                                                },
                                                _ => None,
                                            },
                                        }
                                    },
                                    Some(ProtobufHighlightStyleVariant::CustomIndex(c)) => {
                                        HighlightStyle::CustomIndex {
                                            fg: c.fg.map(|v| v as u8),
                                            bg: c.bg.map(|v| v as u8),
                                        }
                                    },
                                    Some(ProtobufHighlightStyleVariant::BackgroundEmphasis0(_)) => {
                                        HighlightStyle::BackgroundEmphasis0
                                    },
                                    Some(ProtobufHighlightStyleVariant::BackgroundEmphasis1(_)) => {
                                        HighlightStyle::BackgroundEmphasis1
                                    },
                                    Some(ProtobufHighlightStyleVariant::BackgroundEmphasis2(_)) => {
                                        HighlightStyle::BackgroundEmphasis2
                                    },
                                    Some(ProtobufHighlightStyleVariant::BackgroundEmphasis3(_)) => {
                                        HighlightStyle::BackgroundEmphasis3
                                    },
                                    Some(ProtobufHighlightStyleVariant::None(_)) => {
                                        HighlightStyle::None
                                    },
                                    None => HighlightStyle::Emphasis0, // fallback
                                };
                                let context = h
                                    .context
                                    .into_iter()
                                    .map(|item| (item.name, item.value))
                                    .collect();
                                RegexHighlight {
                                    pattern: h.pattern,
                                    style,
                                    layer: match h.layer {
                                        x if x == ProtobufHighlightLayer::Tool as i32 => {
                                            HighlightLayer::Tool
                                        },
                                        x if x == ProtobufHighlightLayer::ActionFeedback as i32 => {
                                            HighlightLayer::ActionFeedback
                                        },
                                        _ => HighlightLayer::Hint, // 0 or unknown => Hint
                                    },
                                    context,
                                    on_hover: h.on_hover,
                                    bold: h.bold,
                                    italic: h.italic,
                                    underline: h.underline,
                                    tooltip_text: h.tooltip_text,
                                }
                            })
                            .collect();
                        Ok(PluginCommand::SetPaneRegexHighlights(pane_id, highlights))
                    },
                    _ => Err("Mismatched payload for SetPaneRegexHighlights"),
                }
            },
            Some(CommandName::ClearPaneHighlights) => match protobuf_plugin_command.payload {
                Some(Payload::ClearPaneHighlightsPayload(p)) => {
                    let pane_id: PaneId = p
                        .pane_id
                        .ok_or("Missing pane_id in ClearPaneHighlights")?
                        .try_into()?;
                    Ok(PluginCommand::ClearPaneHighlights(pane_id))
                },
                _ => Err("Mismatched payload for ClearPaneHighlights"),
            },
            Some(CommandName::OpenPluginPaneFloating) => match protobuf_plugin_command.payload {
                Some(Payload::OpenPluginPaneFloatingPayload(payload)) => {
                    let configuration: BTreeMap<String, String> =
                        payload.configuration.into_iter().collect();
                    let floating_pane_coordinates =
                        payload.floating_pane_coordinates.map(|f| f.into());
                    let context: BTreeMap<String, String> = payload
                        .context
                        .into_iter()
                        .map(|e| (e.name, e.value))
                        .collect();
                    Ok(PluginCommand::OpenPluginPaneFloating {
                        plugin_url: payload.plugin_url,
                        configuration,
                        floating_pane_coordinates,
                        context,
                    })
                },
                _ => Err("Mismatched payload for OpenPluginPaneFloating"),
            },
            None => Err("Unrecognized plugin command"),
        }
    }
}

impl TryFrom<PluginCommand> for ProtobufPluginCommand {
    type Error = &'static str;
    fn try_from(plugin_command: PluginCommand) -> Result<Self, &'static str> {
        match plugin_command {
            PluginCommand::Subscribe(subscriptions) => {
                let subscriptions: ProtobufEventNameList = subscriptions.try_into()?;
                Ok(ProtobufPluginCommand {
                    name: CommandName::Subscribe as i32,
                    payload: Some(Payload::SubscribePayload(SubscribePayload {
                        subscriptions: Some(subscriptions),
                    })),
                })
            },
            PluginCommand::Unsubscribe(subscriptions) => {
                let subscriptions: ProtobufEventNameList = subscriptions.try_into()?;
                Ok(ProtobufPluginCommand {
                    name: CommandName::Unsubscribe as i32,
                    payload: Some(Payload::UnsubscribePayload(UnsubscribePayload {
                        subscriptions: Some(subscriptions),
                    })),
                })
            },
            PluginCommand::SetSelectable(should_be_selectable) => Ok(ProtobufPluginCommand {
                name: CommandName::SetSelectable as i32,
                payload: Some(Payload::SetSelectablePayload(should_be_selectable)),
            }),
            PluginCommand::ShowCursor(cursor_position) => {
                let position = cursor_position.map(|(x, y)| CursorPosition {
                    x: x as u32,
                    y: y as u32,
                });
                Ok(ProtobufPluginCommand {
                    name: CommandName::ShowCursor as i32,
                    payload: Some(Payload::ShowCursorPayload(ShowCursorPayload { position })),
                })
            },
            PluginCommand::GetPluginIds => Ok(ProtobufPluginCommand {
                name: CommandName::GetPluginIds as i32,
                payload: None,
            }),
            PluginCommand::GetZellijVersion => Ok(ProtobufPluginCommand {
                name: CommandName::GetZellijVersion as i32,
                payload: None,
            }),
            PluginCommand::OpenFile(file_to_open, context) => Ok(ProtobufPluginCommand {
                name: CommandName::OpenFile as i32,
                payload: Some(Payload::OpenFilePayload(OpenFilePayload {
                    file_to_open: Some(file_to_open.try_into()?),
                    floating_pane_coordinates: None,
                    context: context
                        .into_iter()
                        .map(|(name, value)| ContextItem { name, value })
                        .collect(),
                })),
            }),
            PluginCommand::OpenFileFloating(file_to_open, floating_pane_coordinates, context) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenFileFloating as i32,
                    payload: Some(Payload::OpenFileFloatingPayload(OpenFilePayload {
                        file_to_open: Some(file_to_open.try_into()?),
                        floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
                        context: context
                            .into_iter()
                            .map(|(name, value)| ContextItem { name, value })
                            .collect(),
                    })),
                })
            },
            PluginCommand::OpenTerminal(cwd) => Ok(ProtobufPluginCommand {
                name: CommandName::OpenTerminal as i32,
                payload: Some(Payload::OpenTerminalPayload(OpenFilePayload {
                    file_to_open: Some(cwd.try_into()?),
                    floating_pane_coordinates: None,
                    context: vec![], // will be added in the future
                })),
            }),
            PluginCommand::OpenTerminalFloating(cwd, floating_pane_coordinates) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenTerminalFloating as i32,
                    payload: Some(Payload::OpenTerminalFloatingPayload(OpenFilePayload {
                        file_to_open: Some(cwd.try_into()?),
                        floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
                        context: vec![], // will be added in the future
                    })),
                })
            },
            PluginCommand::OpenCommandPane(command_to_run, context) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenCommandPane as i32,
                    payload: Some(Payload::OpenCommandPanePayload(OpenCommandPanePayload {
                        command_to_run: Some(command_to_run.try_into()?),
                        floating_pane_coordinates: None,
                        context,
                    })),
                })
            },
            PluginCommand::OpenCommandPaneFloating(
                command_to_run,
                floating_pane_coordinates,
                context,
            ) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenCommandPaneFloating as i32,
                    payload: Some(Payload::OpenCommandPaneFloatingPayload(
                        OpenCommandPanePayload {
                            command_to_run: Some(command_to_run.try_into()?),
                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
                            context,
                        },
                    )),
                })
            },
            PluginCommand::SwitchTabTo(tab_index) => Ok(ProtobufPluginCommand {
                name: CommandName::SwitchTabTo as i32,
                payload: Some(Payload::SwitchTabToPayload(SwitchTabToPayload {
                    tab_index: tab_index,
                })),
            }),
            PluginCommand::SetTimeout(seconds) => Ok(ProtobufPluginCommand {
                name: CommandName::SetTimeout as i32,
                payload: Some(Payload::SetTimeoutPayload(SetTimeoutPayload { seconds })),
            }),
            PluginCommand::ExecCmd(command_line) => Ok(ProtobufPluginCommand {
                name: CommandName::ExecCmd as i32,
                payload: Some(Payload::ExecCmdPayload(ExecCmdPayload { command_line })),
            }),
            PluginCommand::PostMessageTo(plugin_message) => Ok(ProtobufPluginCommand {
                name: CommandName::PostMessageTo as i32,
                payload: Some(Payload::PostMessageToPayload(PluginMessagePayload {
                    message: Some(plugin_message.try_into()?),
                })),
            }),
            PluginCommand::PostMessageToPlugin(plugin_message) => Ok(ProtobufPluginCommand {
                name: CommandName::PostMessageToPlugin as i32,
                payload: Some(Payload::PostMessageToPluginPayload(PluginMessagePayload {
                    message: Some(plugin_message.try_into()?),
                })),
            }),
            PluginCommand::HideSelf => Ok(ProtobufPluginCommand {
                name: CommandName::HideSelf as i32,
                payload: None,
            }),
            PluginCommand::ShowSelf(should_float_if_hidden) => Ok(ProtobufPluginCommand {
                name: CommandName::ShowSelf as i32,
                payload: Some(Payload::ShowSelfPayload(should_float_if_hidden)),
            }),
            PluginCommand::SwitchToMode(input_mode) => Ok(ProtobufPluginCommand {
                name: CommandName::SwitchToMode as i32,
                payload: Some(Payload::SwitchToModePayload(SwitchToModePayload {
                    input_mode: ProtobufInputMode::try_from(input_mode)? as i32,
                })),
            }),
            PluginCommand::NewTabsWithLayout(raw_layout) => Ok(ProtobufPluginCommand {
                name: CommandName::NewTabsWithLayout as i32,
                payload: Some(Payload::NewTabsWithLayoutPayload(raw_layout)),
            }),
            PluginCommand::NewTab { name, cwd } => Ok(ProtobufPluginCommand {
                name: CommandName::NewTab as i32,
                payload: Some(Payload::NewTabPayload(NewTabPayload { name, cwd })),
            }),
            PluginCommand::NewTabUnfocused { name, cwd } => Ok(ProtobufPluginCommand {
                name: CommandName::NewTabUnfocused as i32,
                payload: Some(Payload::NewTabUnfocusedPayload(NewTabUnfocusedPayload {
                    name,
                    cwd,
                })),
            }),
            PluginCommand::NewTiledPaneInTab { tab_position } => Ok(ProtobufPluginCommand {
                name: CommandName::NewTiledPaneInTab as i32,
                payload: Some(Payload::NewTiledPaneInTabPayload(
                    NewTiledPaneInTabPayload {
                        tab_position: tab_position as u32,
                    },
                )),
            }),
            PluginCommand::ToggleFloatingPanes { tab_id } => Ok(ProtobufPluginCommand {
                name: CommandName::ToggleFloatingPanes as i32,
                payload: Some(Payload::ToggleFloatingPanesPayload(
                    ToggleFloatingPanesPayload { tab_id },
                )),
            }),
            PluginCommand::NewPane => Ok(ProtobufPluginCommand {
                name: CommandName::NewPane as i32,
                payload: None,
            }),
            PluginCommand::GoToNextTab => Ok(ProtobufPluginCommand {
                name: CommandName::GoToNextTab as i32,
                payload: None,
            }),
            PluginCommand::GoToPreviousTab => Ok(ProtobufPluginCommand {
                name: CommandName::GoToPreviousTab as i32,
                payload: None,
            }),
            PluginCommand::Resize(resize) => Ok(ProtobufPluginCommand {
                name: CommandName::Resize as i32,
                payload: Some(Payload::ResizePayload(ResizePayload {
                    resize: Some(resize.try_into()?),
                })),
            }),
            PluginCommand::ResizeWithDirection(resize) => Ok(ProtobufPluginCommand {
                name: CommandName::ResizeWithDirection as i32,
                payload: Some(Payload::ResizeWithDirectionPayload(ResizePayload {
                    resize: Some(resize.try_into()?),
                })),
            }),
            PluginCommand::FocusNextPane => Ok(ProtobufPluginCommand {
                name: CommandName::FocusNextPane as i32,
                payload: None,
            }),
            PluginCommand::FocusPreviousPane => Ok(ProtobufPluginCommand {
                name: CommandName::FocusPreviousPane as i32,
                payload: None,
            }),
            PluginCommand::FocusLastPane => Ok(ProtobufPluginCommand {
                name: CommandName::FocusLastPane as i32,
                payload: None,
            }),
            PluginCommand::MoveFocus(direction) => Ok(ProtobufPluginCommand {
                name: CommandName::MoveFocus as i32,
                payload: Some(Payload::MoveFocusPayload(MovePayload {
                    direction: Some(direction.try_into()?),
                })),
            }),
            PluginCommand::MoveFocusOrTab(direction) => Ok(ProtobufPluginCommand {
                name: CommandName::MoveFocusOrTab as i32,
                payload: Some(Payload::MoveFocusOrTabPayload(MovePayload {
                    direction: Some(direction.try_into()?),
                })),
            }),
            PluginCommand::Detach => Ok(ProtobufPluginCommand {
                name: CommandName::Detach as i32,
                payload: None,
            }),
            PluginCommand::EditScrollback => Ok(ProtobufPluginCommand {
                name: CommandName::EditScrollback as i32,
                payload: None,
            }),
            PluginCommand::Write(bytes) => Ok(ProtobufPluginCommand {
                name: CommandName::Write as i32,
                payload: Some(Payload::WritePayload(bytes)),
            }),
            PluginCommand::WriteChars(chars) => Ok(ProtobufPluginCommand {
                name: CommandName::WriteChars as i32,
                payload: Some(Payload::WriteCharsPayload(chars)),
            }),
            PluginCommand::ToggleTab => Ok(ProtobufPluginCommand {
                name: CommandName::ToggleTab as i32,
                payload: None,
            }),
            PluginCommand::MovePane => Ok(ProtobufPluginCommand {
                name: CommandName::MovePane as i32,
                payload: None,
            }),
            PluginCommand::MovePaneWithDirection(direction) => Ok(ProtobufPluginCommand {
                name: CommandName::MovePaneWithDirection as i32,
                payload: Some(Payload::MovePaneWithDirectionPayload(MovePayload {
                    direction: Some(direction.try_into()?),
                })),
            }),
            PluginCommand::ClearScreen => Ok(ProtobufPluginCommand {
                name: CommandName::ClearScreen as i32,
                payload: None,
            }),
            PluginCommand::ScrollUp => Ok(ProtobufPluginCommand {
                name: CommandName::ScrollUp as i32,
                payload: None,
            }),
            PluginCommand::ScrollDown => Ok(ProtobufPluginCommand {
                name: CommandName::ScrollDown as i32,
                payload: None,
            }),
            PluginCommand::ScrollToTop => Ok(ProtobufPluginCommand {
                name: CommandName::ScrollToTop as i32,
                payload: None,
            }),
            PluginCommand::ScrollToBottom => Ok(ProtobufPluginCommand {
                name: CommandName::ScrollToBottom as i32,
                payload: None,
            }),
            PluginCommand::PageScrollUp => Ok(ProtobufPluginCommand {
                name: CommandName::PageScrollUp as i32,
                payload: None,
            }),
            PluginCommand::PageScrollDown => Ok(ProtobufPluginCommand {
                name: CommandName::PageScrollDown as i32,
                payload: None,
            }),
            PluginCommand::ToggleFocusFullscreen => Ok(ProtobufPluginCommand {
                name: CommandName::ToggleFocusFullscreen as i32,
                payload: None,
            }),
            PluginCommand::ToggleFocusNoUiFullscreen => Ok(ProtobufPluginCommand {
                name: CommandName::ToggleFocusNoUiFullscreen as i32,
                payload: None,
            }),
            PluginCommand::FocusHostSession => Ok(ProtobufPluginCommand {
                name: CommandName::FocusHostSession as i32,
                payload: None,
            }),
            PluginCommand::TogglePaneFrames => Ok(ProtobufPluginCommand {
                name: CommandName::TogglePaneFrames as i32,
                payload: None,
            }),
            PluginCommand::SetPaneFrameStyle(pane_frame_style) => Ok(ProtobufPluginCommand {
                name: CommandName::SetPaneFrameStyle as i32,
                payload: Some(Payload::SetPaneFrameStylePayload(
                    ProtobufSetPaneFrameStylePayload {
                        pane_frame_style: ProtobufPaneFrameStyle::try_from(pane_frame_style)?
                            as i32,
                    },
                )),
            }),
            PluginCommand::TogglePaneEmbedOrEject => Ok(ProtobufPluginCommand {
                name: CommandName::TogglePaneEmbedOrEject as i32,
                payload: None,
            }),
            PluginCommand::UndoRenamePane => Ok(ProtobufPluginCommand {
                name: CommandName::UndoRenamePane as i32,
                payload: None,
            }),
            PluginCommand::CloseFocus => Ok(ProtobufPluginCommand {
                name: CommandName::CloseFocus as i32,
                payload: None,
            }),
            PluginCommand::ToggleActiveTabSync => Ok(ProtobufPluginCommand {
                name: CommandName::ToggleActiveTabSync as i32,
                payload: None,
            }),
            PluginCommand::CloseFocusedTab => Ok(ProtobufPluginCommand {
                name: CommandName::CloseFocusedTab as i32,
                payload: None,
            }),
            PluginCommand::UndoRenameTab => Ok(ProtobufPluginCommand {
                name: CommandName::UndoRenameTab as i32,
                payload: None,
            }),
            PluginCommand::QuitZellij => Ok(ProtobufPluginCommand {
                name: CommandName::QuitZellij as i32,
                payload: None,
            }),
            PluginCommand::PreviousSwapLayout => Ok(ProtobufPluginCommand {
                name: CommandName::PreviousSwapLayout as i32,
                payload: None,
            }),
            PluginCommand::NextSwapLayout => Ok(ProtobufPluginCommand {
                name: CommandName::NextSwapLayout as i32,
                payload: None,
            }),
            PluginCommand::GoToTabName(tab_name) => Ok(ProtobufPluginCommand {
                name: CommandName::GoToTabName as i32,
                payload: Some(Payload::GoToTabNamePayload(tab_name)),
            }),
            PluginCommand::FocusOrCreateTab(tab_name) => Ok(ProtobufPluginCommand {
                name: CommandName::FocusOrCreateTab as i32,
                payload: Some(Payload::FocusOrCreateTabPayload(tab_name)),
            }),
            PluginCommand::GoToTab(tab_index) => Ok(ProtobufPluginCommand {
                name: CommandName::GoToTab as i32,
                payload: Some(Payload::GoToTabPayload(tab_index)),
            }),
            PluginCommand::StartOrReloadPlugin(url) => Ok(ProtobufPluginCommand {
                name: CommandName::StartOrReloadPlugin as i32,
                payload: Some(Payload::StartOrReloadPluginPayload(url)),
            }),
            PluginCommand::CloseTerminalPane(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::CloseTerminalPane as i32,
                payload: Some(Payload::CloseTerminalPanePayload(pane_id)),
            }),
            PluginCommand::ClosePluginPane(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::ClosePluginPane as i32,
                payload: Some(Payload::ClosePluginPanePayload(pane_id)),
            }),
            PluginCommand::FocusTerminalPane(
                pane_id,
                should_float_if_hidden,
                should_be_in_place_if_hidden,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::FocusTerminalPane as i32,
                payload: Some(Payload::FocusTerminalPanePayload(PaneIdAndShouldFloat {
                    pane_id: pane_id,
                    should_float: should_float_if_hidden,
                    should_be_in_place: should_be_in_place_if_hidden,
                })),
            }),
            PluginCommand::FocusPluginPane(
                pane_id,
                should_float_if_hidden,
                should_be_in_place_if_hidden,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::FocusPluginPane as i32,
                payload: Some(Payload::FocusPluginPanePayload(PaneIdAndShouldFloat {
                    pane_id: pane_id,
                    should_float: should_float_if_hidden,
                    should_be_in_place: should_be_in_place_if_hidden,
                })),
            }),
            PluginCommand::RenameTerminalPane(pane_id, new_name) => Ok(ProtobufPluginCommand {
                name: CommandName::RenameTerminalPane as i32,
                payload: Some(Payload::RenameTerminalPanePayload(IdAndNewName {
                    id: pane_id,
                    new_name,
                })),
            }),
            PluginCommand::RenamePluginPane(pane_id, new_name) => Ok(ProtobufPluginCommand {
                name: CommandName::RenamePluginPane as i32,
                payload: Some(Payload::RenamePluginPanePayload(IdAndNewName {
                    id: pane_id,
                    new_name,
                })),
            }),
            PluginCommand::RenameTab(tab_index, new_name) => Ok(ProtobufPluginCommand {
                name: CommandName::RenameTab as i32,
                payload: Some(Payload::RenameTabPayload(IdAndNewName {
                    id: tab_index,
                    new_name,
                })),
            }),
            PluginCommand::ReportPanic(payload) => Ok(ProtobufPluginCommand {
                name: CommandName::ReportCrash as i32,
                payload: Some(Payload::ReportCrashPayload(payload)),
            }),
            PluginCommand::RequestPluginPermissions(permissions) => Ok(ProtobufPluginCommand {
                name: CommandName::RequestPluginPermissions as i32,
                payload: Some(Payload::RequestPluginPermissionPayload(
                    RequestPluginPermissionPayload {
                        permissions: permissions
                            .iter()
                            .filter_map(|p| ProtobufPermissionType::try_from(*p).ok())
                            .map(|p| p as i32)
                            .collect(),
                    },
                )),
            }),
            PluginCommand::SwitchSession(switch_to_session) => Ok(ProtobufPluginCommand {
                name: CommandName::SwitchSession as i32,
                payload: Some(Payload::SwitchSessionPayload(SwitchSessionPayload {
                    name: switch_to_session.name,
                    tab_position: switch_to_session.tab_position.map(|t| t as u32),
                    pane_id: switch_to_session.pane_id.map(|p| p.0),
                    pane_id_is_plugin: switch_to_session.pane_id.map(|p| p.1),
                    layout: switch_to_session.layout.and_then(|l| l.try_into().ok()),
                    cwd: switch_to_session.cwd.map(|c| c.display().to_string()),
                })),
            }),
            PluginCommand::OpenTerminalInPlace(cwd) => Ok(ProtobufPluginCommand {
                name: CommandName::OpenTerminalInPlace as i32,
                payload: Some(Payload::OpenTerminalInPlacePayload(OpenFilePayload {
                    file_to_open: Some(cwd.try_into()?),
                    floating_pane_coordinates: None,
                    context: vec![], // will be added in the future
                })),
            }),
            PluginCommand::OpenFileInPlace(file_to_open, context) => Ok(ProtobufPluginCommand {
                name: CommandName::OpenFileInPlace as i32,
                payload: Some(Payload::OpenFileInPlacePayload(OpenFilePayload {
                    file_to_open: Some(file_to_open.try_into()?),
                    floating_pane_coordinates: None,
                    context: context
                        .into_iter()
                        .map(|(name, value)| ContextItem { name, value })
                        .collect(),
                })),
            }),
            PluginCommand::OpenCommandPaneInPlace(command_to_run, context) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenCommandInPlace as i32,
                    payload: Some(Payload::OpenCommandPaneInPlacePayload(
                        OpenCommandPanePayload {
                            command_to_run: Some(command_to_run.try_into()?),
                            floating_pane_coordinates: None,
                            context,
                        },
                    )),
                })
            },
            PluginCommand::RunCommand(command_line, env_variables, cwd, context) => {
                let env_variables: Vec<_> = env_variables
                    .into_iter()
                    .map(|(name, value)| EnvVariable { name, value })
                    .collect();
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                let cwd = cwd.display().to_string();
                Ok(ProtobufPluginCommand {
                    name: CommandName::RunCommand as i32,
                    payload: Some(Payload::RunCommandPayload(RunCommandPayload {
                        command_line,
                        env_variables,
                        cwd,
                        context,
                    })),
                })
            },
            PluginCommand::WebRequest(url, verb, headers, body, context) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                let headers: Vec<_> = headers
                    .into_iter()
                    .map(|(name, value)| Header { name, value })
                    .collect();
                let verb: ProtobufHttpVerb = verb.into();
                Ok(ProtobufPluginCommand {
                    name: CommandName::WebRequest as i32,
                    payload: Some(Payload::WebRequestPayload(WebRequestPayload {
                        url,
                        verb: verb as i32,
                        body,
                        headers,
                        context,
                    })),
                })
            },
            PluginCommand::DeleteDeadSession(dead_session_name) => Ok(ProtobufPluginCommand {
                name: CommandName::DeleteDeadSession as i32,
                payload: Some(Payload::DeleteDeadSessionPayload(dead_session_name)),
            }),
            PluginCommand::DeleteAllDeadSessions => Ok(ProtobufPluginCommand {
                name: CommandName::DeleteAllDeadSessions as i32,
                payload: None,
            }),
            PluginCommand::RenameSession(new_session_name) => Ok(ProtobufPluginCommand {
                name: CommandName::RenameSession as i32,
                payload: Some(Payload::RenameSessionPayload(new_session_name)),
            }),
            PluginCommand::UnblockCliPipeInput(pipe_name) => Ok(ProtobufPluginCommand {
                name: CommandName::UnblockCliPipeInput as i32,
                payload: Some(Payload::UnblockCliPipeInputPayload(pipe_name)),
            }),
            PluginCommand::BlockCliPipeInput(pipe_name) => Ok(ProtobufPluginCommand {
                name: CommandName::BlockCliPipeInput as i32,
                payload: Some(Payload::BlockCliPipeInputPayload(pipe_name)),
            }),
            PluginCommand::CliPipeOutput(pipe_name, output) => Ok(ProtobufPluginCommand {
                name: CommandName::CliPipeOutput as i32,
                payload: Some(Payload::CliPipeOutputPayload(CliPipeOutputPayload {
                    pipe_name,
                    output,
                })),
            }),
            PluginCommand::MessageToPlugin(message_to_plugin) => {
                let plugin_config: Vec<_> = message_to_plugin
                    .plugin_config
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                let message_args: Vec<_> = message_to_plugin
                    .message_args
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::MessageToPlugin as i32,
                    payload: Some(Payload::MessageToPluginPayload(MessageToPluginPayload {
                        plugin_url: message_to_plugin.plugin_url,
                        plugin_config,
                        message_name: message_to_plugin.message_name,
                        message_payload: message_to_plugin.message_payload,
                        message_args,
                        new_plugin_args: message_to_plugin.new_plugin_args.map(|m_t_p| {
                            ProtobufNewPluginArgs {
                                should_float: m_t_p.should_float,
                                pane_id_to_replace: m_t_p
                                    .pane_id_to_replace
                                    .and_then(|p_id| ProtobufPaneId::try_from(p_id).ok()),
                                pane_title: m_t_p.pane_title,
                                cwd: m_t_p.cwd.map(|cwd| cwd.display().to_string()),
                                skip_cache: m_t_p.skip_cache,
                                should_focus: m_t_p.should_focus,
                            }
                        }),
                        destination_plugin_id: message_to_plugin.destination_plugin_id,
                        floating_pane_coordinates: message_to_plugin
                            .floating_pane_coordinates
                            .and_then(|f| f.try_into().ok()),
                    })),
                })
            },
            PluginCommand::DisconnectOtherClients => Ok(ProtobufPluginCommand {
                name: CommandName::DisconnectOtherClients as i32,
                payload: None,
            }),
            PluginCommand::KillSessions(session_names) => Ok(ProtobufPluginCommand {
                name: CommandName::KillSessions as i32,
                payload: Some(Payload::KillSessionsPayload(KillSessionsPayload {
                    session_names,
                })),
            }),
            PluginCommand::ScanHostFolder(folder_to_scan) => Ok(ProtobufPluginCommand {
                name: CommandName::ScanHostFolder as i32,
                payload: Some(Payload::ScanHostFolderPayload(
                    folder_to_scan.display().to_string(),
                )),
            }),
            PluginCommand::WatchFilesystem => Ok(ProtobufPluginCommand {
                name: CommandName::WatchFilesystem as i32,
                payload: None,
            }),
            PluginCommand::ListWindowsVolumes => Ok(ProtobufPluginCommand {
                name: CommandName::ListWindowsVolumes as i32,
                payload: None,
            }),
            PluginCommand::GetSessionList => Ok(ProtobufPluginCommand {
                name: CommandName::GetSessionList as i32,
                payload: Some(Payload::GetSessionListPayload(
                    ProtobufGetSessionListPayload {},
                )),
            }),
            PluginCommand::KillSessionsAndReply(session_names) => Ok(ProtobufPluginCommand {
                name: CommandName::KillSessionsAndReply as i32,
                payload: Some(Payload::KillSessionsAndReplyPayload(KillSessionsPayload {
                    session_names,
                })),
            }),
            PluginCommand::DeleteDeadSessionAndReply(dead_session_name) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::DeleteDeadSessionAndReply as i32,
                    payload: Some(Payload::DeleteDeadSessionAndReplyPayload(dead_session_name)),
                })
            },
            PluginCommand::DeleteAllDeadSessionsAndReply => Ok(ProtobufPluginCommand {
                name: CommandName::DeleteAllDeadSessionsAndReply as i32,
                payload: None,
            }),
            PluginCommand::SetSoftKeyboard(on) => Ok(ProtobufPluginCommand {
                name: CommandName::SetSoftKeyboard as i32,
                payload: Some(Payload::SetSoftKeyboardPayload(
                    ProtobufSetSoftKeyboardPayload { on },
                )),
            }),
            PluginCommand::DumpSessionLayout { tab_index } => Ok(ProtobufPluginCommand {
                name: CommandName::DumpSessionLayout as i32,
                payload: tab_index.map(|idx| {
                    Payload::DumpSessionLayoutPayload(DumpSessionLayoutPayload {
                        tab_index: Some(idx as u32),
                    })
                }),
            }),
            PluginCommand::CloseSelf => Ok(ProtobufPluginCommand {
                name: CommandName::CloseSelf as i32,
                payload: None,
            }),
            PluginCommand::NewTabsWithLayoutInfo(new_tabs_with_layout_info_payload) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::NewTabsWithLayoutInfo as i32,
                    payload: Some(Payload::NewTabsWithLayoutInfoPayload(
                        NewTabsWithLayoutInfoPayload {
                            layout_info: new_tabs_with_layout_info_payload.try_into().ok(),
                        },
                    )),
                })
            },
            PluginCommand::Reconfigure(config, write_to_disk) => Ok(ProtobufPluginCommand {
                name: CommandName::Reconfigure as i32,
                payload: Some(Payload::ReconfigurePayload(ReconfigurePayload {
                    config,
                    write_to_disk,
                })),
            }),
            PluginCommand::HidePaneWithId(pane_id_to_hide) => Ok(ProtobufPluginCommand {
                name: CommandName::HidePaneWithId as i32,
                payload: Some(Payload::HidePaneWithIdPayload(HidePaneWithIdPayload {
                    pane_id: ProtobufPaneId::try_from(pane_id_to_hide).ok(),
                })),
            }),
            PluginCommand::ShowPaneWithId(
                pane_id_to_show,
                should_float_if_hidden,
                should_focus_pane,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::ShowPaneWithId as i32,
                payload: Some(Payload::ShowPaneWithIdPayload(ShowPaneWithIdPayload {
                    pane_id: ProtobufPaneId::try_from(pane_id_to_show).ok(),
                    should_float_if_hidden,
                    should_focus_pane,
                })),
            }),
            PluginCommand::OpenCommandPaneBackground(command_to_run, context) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenCommandPaneBackground as i32,
                    payload: Some(Payload::OpenCommandPaneBackgroundPayload(
                        OpenCommandPanePayload {
                            command_to_run: Some(command_to_run.try_into()?),
                            floating_pane_coordinates: None,
                            context,
                        },
                    )),
                })
            },
            PluginCommand::RerunCommandPane(terminal_pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::RerunCommandPane as i32,
                payload: Some(Payload::RerunCommandPanePayload(RerunCommandPanePayload {
                    terminal_pane_id,
                })),
            }),
            PluginCommand::ResizePaneIdWithDirection(resize, pane_id) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::ResizePaneIdWithDirection as i32,
                    payload: Some(Payload::ResizePaneIdWithDirectionPayload(
                        ResizePaneIdWithDirectionPayload {
                            resize: Some(resize.try_into()?),
                            pane_id: Some(pane_id.try_into()?),
                        },
                    )),
                })
            },
            PluginCommand::EditScrollbackForPaneWithId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::EditScrollbackForPaneWithId as i32,
                payload: Some(Payload::EditScrollbackForPaneWithIdPayload(
                    EditScrollbackForPaneWithIdPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::GetPaneScrollback {
                pane_id,
                get_full_scrollback,
            } => Ok(ProtobufPluginCommand {
                name: CommandName::GetPaneScrollback as i32,
                payload: Some(Payload::GetPaneScrollbackPayload(
                    GetPaneScrollbackPayload {
                        pane_id: Some(pane_id.try_into()?),
                        get_full_scrollback,
                    },
                )),
            }),
            PluginCommand::WriteToPaneId(bytes_to_write, pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::WriteToPaneId as i32,
                payload: Some(Payload::WriteToPaneIdPayload(WriteToPaneIdPayload {
                    bytes_to_write,
                    pane_id: Some(pane_id.try_into()?),
                })),
            }),
            PluginCommand::WriteCharsToPaneId(chars_to_write, pane_id) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::WriteCharsToPaneId as i32,
                    payload: Some(Payload::WriteCharsToPaneIdPayload(
                        WriteCharsToPaneIdPayload {
                            chars_to_write,
                            pane_id: Some(pane_id.try_into()?),
                        },
                    )),
                })
            },
            PluginCommand::SendSigintToPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::SendSigintToPaneId as i32,
                payload: Some(Payload::SendSigintToPaneIdPayload(pane_id.try_into()?)),
            }),
            PluginCommand::SendSigkillToPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::SendSigkillToPaneId as i32,
                payload: Some(Payload::SendSigkillToPaneIdPayload(pane_id.try_into()?)),
            }),
            PluginCommand::GetPanePid { pane_id } => Ok(ProtobufPluginCommand {
                name: CommandName::GetPanePid as i32,
                payload: Some(Payload::GetPanePidPayload(GetPanePidPayload {
                    pane_id: Some(pane_id.try_into()?),
                })),
            }),
            PluginCommand::GetPaneRunningCommand { pane_id } => Ok(ProtobufPluginCommand {
                name: CommandName::GetPaneRunningCommand as i32,
                payload: Some(Payload::GetPaneRunningCommandPayload(
                    ProtobufGetPaneRunningCommandPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::GetPaneCwd { pane_id } => Ok(ProtobufPluginCommand {
                name: CommandName::GetPaneCwd as i32,
                payload: Some(Payload::GetPaneCwdPayload(ProtobufGetPaneCwdPayload {
                    pane_id: Some(pane_id.try_into()?),
                })),
            }),
            PluginCommand::OverrideLayout(
                layout_info,
                retain_existing_terminal_panes,
                retain_existing_plugin_panes,
                apply_only_to_active_tab,
                context,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::OverrideLayout as i32,
                payload: Some(Payload::OverrideLayoutPayload(OverrideLayoutPayload {
                    layout_info: layout_info.try_into().ok(),
                    context: context
                        .into_iter()
                        .map(|(name, value)| ContextItem { name, value })
                        .collect(),
                    retain_existing_terminal_panes,
                    retain_existing_plugin_panes,
                    apply_only_to_active_tab,
                })),
            }),
            PluginCommand::SaveLayout {
                layout_name,
                layout_kdl,
                overwrite,
            } => Ok(ProtobufPluginCommand {
                name: CommandName::SaveLayout as i32,
                payload: Some(Payload::SaveLayoutPayload(SaveLayoutPayload {
                    layout_name,
                    layout_kdl,
                    overwrite,
                })),
            }),
            PluginCommand::DeleteLayout { layout_name } => Ok(ProtobufPluginCommand {
                name: CommandName::DeleteLayout as i32,
                payload: Some(Payload::DeleteLayoutPayload(DeleteLayoutPayload {
                    layout_name,
                })),
            }),
            PluginCommand::RenameLayout {
                old_layout_name,
                new_layout_name,
            } => Ok(ProtobufPluginCommand {
                name: CommandName::RenameLayout as i32,
                payload: Some(Payload::RenameLayoutPayload(RenameLayoutPayload {
                    old_layout_name,
                    new_layout_name,
                })),
            }),
            PluginCommand::EditLayout {
                layout_name,
                context,
            } => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::EditLayout as i32,
                    payload: Some(Payload::EditLayoutPayload(EditLayoutPayload {
                        layout_name,
                        context,
                    })),
                })
            },
            PluginCommand::MovePaneWithPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::MovePaneWithPaneId as i32,
                payload: Some(Payload::MovePaneWithPaneIdPayload(
                    MovePaneWithPaneIdPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::MovePaneWithPaneIdInDirection(pane_id, direction) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::MovePaneWithPaneIdInDirection as i32,
                    payload: Some(Payload::MovePaneWithPaneIdInDirectionPayload(
                        MovePaneWithPaneIdInDirectionPayload {
                            pane_id: Some(pane_id.try_into()?),
                            direction: Some(direction.try_into()?),
                        },
                    )),
                })
            },
            PluginCommand::ClearScreenForPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::ClearScreenForPaneId as i32,
                payload: Some(Payload::ClearScreenForPaneIdPayload(
                    ClearScreenForPaneIdPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::ScrollUpInPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::ScrollUpInPaneId as i32,
                payload: Some(Payload::ScrollUpInPaneIdPayload(ScrollUpInPaneIdPayload {
                    pane_id: Some(pane_id.try_into()?),
                })),
            }),
            PluginCommand::ScrollDownInPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::ScrollDownInPaneId as i32,
                payload: Some(Payload::ScrollDownInPaneIdPayload(
                    ScrollDownInPaneIdPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::ScrollToTopInPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::ScrollToTopInPaneId as i32,
                payload: Some(Payload::ScrollToTopInPaneIdPayload(
                    ScrollToTopInPaneIdPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::ScrollToBottomInPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::ScrollToBottomInPaneId as i32,
                payload: Some(Payload::ScrollToBottomInPaneIdPayload(
                    ScrollToBottomInPaneIdPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::PageScrollUpInPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::PageScrollUpInPaneId as i32,
                payload: Some(Payload::PageScrollUpInPaneIdPayload(
                    PageScrollUpInPaneIdPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::PageScrollDownInPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::PageScrollDownInPaneId as i32,
                payload: Some(Payload::PageScrollDownInPaneIdPayload(
                    PageScrollDownInPaneIdPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::TogglePaneIdFullscreen(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::TogglePaneIdFullscreen as i32,
                payload: Some(Payload::TogglePaneIdFullscreenPayload(
                    TogglePaneIdFullscreenPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::TogglePaneEmbedOrEjectForPaneId(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::TogglePaneEmbedOrEjectForPaneId as i32,
                payload: Some(Payload::TogglePaneEmbedOrEjectForPaneIdPayload(
                    TogglePaneEmbedOrEjectForPaneIdPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::CloseTabWithIndex(tab_index) => Ok(ProtobufPluginCommand {
                name: CommandName::CloseTabWithIndex as i32,
                payload: Some(Payload::CloseTabWithIndexPayload(
                    CloseTabWithIndexPayload {
                        tab_index: tab_index as u32,
                    },
                )),
            }),
            PluginCommand::BreakPanesToNewTab(
                pane_ids,
                new_tab_name,
                should_change_focus_to_new_tab,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::BreakPanesToNewTab as i32,
                payload: Some(Payload::BreakPanesToNewTabPayload(
                    BreakPanesToNewTabPayload {
                        pane_ids: pane_ids
                            .into_iter()
                            .filter_map(|p_id| p_id.try_into().ok())
                            .collect(),
                        should_change_focus_to_new_tab,
                        new_tab_name,
                    },
                )),
            }),
            PluginCommand::BreakPanesToTabWithIndex(
                pane_ids,
                tab_index,
                should_change_focus_to_target_tab,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::BreakPanesToTabWithIndex as i32,
                payload: Some(Payload::BreakPanesToTabWithIndexPayload(
                    BreakPanesToTabWithIndexPayload {
                        pane_ids: pane_ids
                            .into_iter()
                            .filter_map(|p_id| p_id.try_into().ok())
                            .collect(),
                        tab_index: tab_index as u32,
                        should_change_focus_to_target_tab,
                    },
                )),
            }),
            PluginCommand::SwitchTabToId(tab_id) => Ok(ProtobufPluginCommand {
                name: CommandName::SwitchTabToId as i32,
                payload: Some(Payload::SwitchTabToIdPayload(SwitchTabToIdPayload {
                    tab_id,
                })),
            }),
            PluginCommand::GoToTabWithId(tab_id) => Ok(ProtobufPluginCommand {
                name: CommandName::GoToTabWithId as i32,
                payload: Some(Payload::GoToTabWithIdPayload(GoToTabWithIdPayload {
                    tab_id,
                })),
            }),
            PluginCommand::CloseTabWithId(tab_id) => Ok(ProtobufPluginCommand {
                name: CommandName::CloseTabWithId as i32,
                payload: Some(Payload::CloseTabWithIdPayload(CloseTabWithIdPayload {
                    tab_id,
                })),
            }),
            PluginCommand::RenameTabWithId(tab_id, new_name) => Ok(ProtobufPluginCommand {
                name: CommandName::RenameTabWithId as i32,
                payload: Some(Payload::RenameTabWithIdPayload(RenameTabWithIdPayload {
                    tab_id,
                    new_name,
                })),
            }),
            PluginCommand::BreakPanesToTabWithId(
                pane_ids,
                tab_id,
                should_change_focus_to_target_tab,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::BreakPanesToTabWithId as i32,
                payload: Some(Payload::BreakPanesToTabWithIdPayload(
                    BreakPanesToTabWithIdPayload {
                        pane_ids: pane_ids
                            .into_iter()
                            .filter_map(|p_id| p_id.try_into().ok())
                            .collect(),
                        tab_id,
                        should_change_focus_to_target_tab,
                    },
                )),
            }),
            PluginCommand::ReloadPlugin(plugin_id) => Ok(ProtobufPluginCommand {
                name: CommandName::ReloadPlugin as i32,
                payload: Some(Payload::ReloadPluginPayload(ReloadPluginPayload {
                    plugin_id,
                })),
            }),
            PluginCommand::LoadNewPlugin {
                url,
                config,
                load_in_background,
                skip_plugin_cache,
            } => Ok(ProtobufPluginCommand {
                name: CommandName::LoadNewPlugin as i32,
                payload: Some(Payload::LoadNewPluginPayload(LoadNewPluginPayload {
                    plugin_url: url,
                    plugin_config: config
                        .into_iter()
                        .map(|(name, value)| ContextItem { name, value })
                        .collect(),
                    should_skip_plugin_cache: skip_plugin_cache,
                    should_load_plugin_in_background: load_in_background,
                })),
            }),
            PluginCommand::RebindKeys {
                keys_to_rebind,
                keys_to_unbind,
                write_config_to_disk,
            } => Ok(ProtobufPluginCommand {
                name: CommandName::RebindKeys as i32,
                payload: Some(Payload::RebindKeysPayload(RebindKeysPayload {
                    keys_to_rebind: keys_to_rebind
                        .into_iter()
                        .filter_map(|k| k.try_into().ok())
                        .collect(),
                    keys_to_unbind: keys_to_unbind
                        .into_iter()
                        .filter_map(|k| k.try_into().ok())
                        .collect(),
                    write_config_to_disk,
                })),
            }),
            PluginCommand::ListClients => Ok(ProtobufPluginCommand {
                name: CommandName::ListClients as i32,
                payload: None,
            }),
            PluginCommand::ChangeHostFolder(new_host_folder) => Ok(ProtobufPluginCommand {
                name: CommandName::ChangeHostFolder as i32,
                payload: Some(Payload::ChangeHostFolderPayload(ChangeHostFolderPayload {
                    new_host_folder: new_host_folder.display().to_string(), // TODO: not accurate?
                })),
            }),
            PluginCommand::SetFloatingPanePinned(pane_id, should_be_pinned) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::SetFloatingPanePinned as i32,
                    payload: Some(Payload::SetFloatingPanePinnedPayload(
                        SetFloatingPanePinnedPayload {
                            pane_id: pane_id.try_into().ok(),
                            should_be_pinned,
                        },
                    )),
                })
            },
            PluginCommand::StackPanes(pane_ids) => Ok(ProtobufPluginCommand {
                name: CommandName::StackPanes as i32,
                payload: Some(Payload::StackPanesPayload(StackPanesPayload {
                    pane_ids: pane_ids
                        .into_iter()
                        .filter_map(|p_id| p_id.try_into().ok())
                        .collect(),
                })),
            }),
            PluginCommand::ChangeFloatingPanesCoordinates(
                pane_ids_and_floating_panes_coordinates,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::ChangeFloatingPanesCoordinates as i32,
                payload: Some(Payload::ChangeFloatingPanesCoordinatesPayload(
                    ChangeFloatingPanesCoordinatesPayload {
                        pane_ids_and_floating_panes_coordinates:
                            pane_ids_and_floating_panes_coordinates
                                .into_iter()
                                .filter_map(|(p_id, floating_pane_coordinates)| {
                                    Some(PaneIdAndFloatingPaneCoordinates {
                                        pane_id: Some(p_id.try_into().ok()?),
                                        floating_pane_coordinates: Some(
                                            floating_pane_coordinates.try_into().ok()?,
                                        ),
                                    })
                                })
                                .collect(),
                    },
                )),
            }),
            PluginCommand::TogglePaneBorderless(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::TogglePaneBorderless as i32,
                payload: Some(Payload::TogglePaneBorderlessPayload(
                    TogglePaneBorderlessPayload {
                        pane_id: Some(pane_id.try_into()?),
                    },
                )),
            }),
            PluginCommand::SetPaneBorderless(pane_id, borderless) => Ok(ProtobufPluginCommand {
                name: CommandName::SetPaneBorderless as i32,
                payload: Some(Payload::SetPaneBorderlessPayload(
                    SetPaneBorderlessPayload {
                        pane_id: Some(pane_id.try_into()?),
                        borderless,
                    },
                )),
            }),
            PluginCommand::OpenCommandPaneNearPlugin(command_to_run, context) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenCommandPaneNearPlugin as i32,
                    payload: Some(Payload::OpenCommandPaneNearPluginPayload(
                        OpenCommandPaneNearPluginPayload {
                            command_to_run: Some(command_to_run.try_into()?),
                            floating_pane_coordinates: None,
                            context,
                        },
                    )),
                })
            },
            PluginCommand::OpenCommandPaneFloatingNearPlugin(
                command_to_run,
                floating_pane_coordinates,
                context,
            ) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenCommandPaneFloatingNearPlugin as i32,
                    payload: Some(Payload::OpenCommandPaneFloatingNearPluginPayload(
                        OpenCommandPaneFloatingNearPluginPayload {
                            command_to_run: Some(command_to_run.try_into()?),
                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
                            context,
                        },
                    )),
                })
            },
            PluginCommand::OpenTerminalNearPlugin(cwd) => Ok(ProtobufPluginCommand {
                name: CommandName::OpenTerminalNearPlugin as i32,
                payload: Some(Payload::OpenTerminalNearPluginPayload(
                    OpenTerminalNearPluginPayload {
                        file_to_open: Some(cwd.try_into()?),
                        context: vec![], // will be added in the future
                    },
                )),
            }),
            PluginCommand::OpenTerminalFloatingNearPlugin(cwd, floating_pane_coordinates) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenTerminalFloatingNearPlugin as i32,
                    payload: Some(Payload::OpenTerminalFloatingNearPluginPayload(
                        OpenTerminalFloatingNearPluginPayload {
                            file_to_open: Some(cwd.try_into()?),
                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
                            context: vec![], // will be added in the future
                        },
                    )),
                })
            },
            PluginCommand::OpenTerminalInPlaceOfPlugin(cwd, close_plugin_after_replace) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenTerminalInPlaceOfPlugin as i32,
                    payload: Some(Payload::OpenTerminalInPlaceOfPluginPayload(
                        OpenTerminalInPlaceOfPluginPayload {
                            file_to_open: Some(cwd.try_into()?),
                            close_plugin_after_replace,
                            context: vec![], // will be added in the future
                        },
                    )),
                })
            },
            PluginCommand::OpenCommandPaneInPlaceOfPlugin(
                command_to_run,
                close_plugin_after_replace,
                context,
            ) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenCommandPaneInPlaceOfPlugin as i32,
                    payload: Some(Payload::OpenCommandPaneInPlaceOfPluginPayload(
                        OpenCommandPaneInPlaceOfPluginPayload {
                            command_to_run: Some(command_to_run.try_into()?),
                            close_plugin_after_replace,
                            context,
                        },
                    )),
                })
            },
            PluginCommand::OpenFileNearPlugin(file_to_open, context) => Ok(ProtobufPluginCommand {
                name: CommandName::OpenFileNearPlugin as i32,
                payload: Some(Payload::OpenFileNearPluginPayload(
                    OpenFileNearPluginPayload {
                        file_to_open: Some(file_to_open.try_into()?),
                        floating_pane_coordinates: None,
                        context: context
                            .into_iter()
                            .map(|(name, value)| ContextItem { name, value })
                            .collect(),
                    },
                )),
            }),
            PluginCommand::OpenFileFloatingNearPlugin(
                file_to_open,
                floating_pane_coordinates,
                context,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::OpenFileFloatingNearPlugin as i32,
                payload: Some(Payload::OpenFileFloatingNearPluginPayload(
                    OpenFileFloatingNearPluginPayload {
                        file_to_open: Some(file_to_open.try_into()?),
                        floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
                        context: context
                            .into_iter()
                            .map(|(name, value)| ContextItem { name, value })
                            .collect(),
                    },
                )),
            }),
            PluginCommand::OpenFileInPlaceOfPlugin(
                file_to_open,
                close_plugin_after_replace,
                context,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::OpenFileInPlaceOfPlugin as i32,
                payload: Some(Payload::OpenFileInPlaceOfPluginPayload(
                    OpenFileInPlaceOfPluginPayload {
                        file_to_open: Some(file_to_open.try_into()?),
                        floating_pane_coordinates: None,
                        close_plugin_after_replace,
                        context: context
                            .into_iter()
                            .map(|(name, value)| ContextItem { name, value })
                            .collect(),
                    },
                )),
            }),
            PluginCommand::GroupAndUngroupPanes(
                panes_to_group,
                panes_to_ungroup,
                for_all_clients,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::GroupAndUngroupPanes as i32,
                payload: Some(Payload::GroupAndUngroupPanesPayload(
                    GroupAndUngroupPanesPayload {
                        pane_ids_to_group: panes_to_group
                            .iter()
                            .filter_map(|&p| p.try_into().ok())
                            .collect(),
                        pane_ids_to_ungroup: panes_to_ungroup
                            .iter()
                            .filter_map(|&p| p.try_into().ok())
                            .collect(),
                        for_all_clients,
                    },
                )),
            }),
            PluginCommand::StartWebServer => Ok(ProtobufPluginCommand {
                name: CommandName::StartWebServer as i32,
                payload: None,
            }),
            PluginCommand::StopWebServer => Ok(ProtobufPluginCommand {
                name: CommandName::StopWebServer as i32,
                payload: None,
            }),
            PluginCommand::QueryWebServerStatus => Ok(ProtobufPluginCommand {
                name: CommandName::QueryWebServerStatus as i32,
                payload: None,
            }),
            PluginCommand::HighlightAndUnhighlightPanes(
                panes_to_highlight,
                panes_to_unhighlight,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::HighlightAndUnhighlightPanes as i32,
                payload: Some(Payload::HighlightAndUnhighlightPanesPayload(
                    HighlightAndUnhighlightPanesPayload {
                        pane_ids_to_highlight: panes_to_highlight
                            .iter()
                            .filter_map(|&p| p.try_into().ok())
                            .collect(),
                        pane_ids_to_unhighlight: panes_to_unhighlight
                            .iter()
                            .filter_map(|&p| p.try_into().ok())
                            .collect(),
                    },
                )),
            }),
            PluginCommand::CloseMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand {
                name: CommandName::CloseMultiplePanes as i32,
                payload: Some(Payload::CloseMultiplePanesPayload(
                    CloseMultiplePanesPayload {
                        pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(),
                    },
                )),
            }),
            PluginCommand::FloatMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand {
                name: CommandName::FloatMultiplePanes as i32,
                payload: Some(Payload::FloatMultiplePanesPayload(
                    FloatMultiplePanesPayload {
                        pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(),
                    },
                )),
            }),
            PluginCommand::EmbedMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand {
                name: CommandName::EmbedMultiplePanes as i32,
                payload: Some(Payload::EmbedMultiplePanesPayload(
                    EmbedMultiplePanesPayload {
                        pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(),
                    },
                )),
            }),
            PluginCommand::ShareCurrentSession => Ok(ProtobufPluginCommand {
                name: CommandName::ShareCurrentSession as i32,
                payload: None,
            }),
            PluginCommand::StopSharingCurrentSession => Ok(ProtobufPluginCommand {
                name: CommandName::StopSharingCurrentSession as i32,
                payload: None,
            }),
            PluginCommand::SetSelfMouseSelectionSupport(support_mouse_selection) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::SetSelfMouseSelectionSupport as i32,
                    payload: Some(Payload::SetSelfMouseSelectionSupportPayload(
                        SetSelfMouseSelectionSupportPayload {
                            support_mouse_selection,
                        },
                    )),
                })
            },
            PluginCommand::GenerateWebLoginToken(token_label, read_only) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::GenerateWebLoginToken as i32,
                    payload: Some(Payload::GenerateWebLoginTokenPayload(
                        GenerateWebLoginTokenPayload {
                            token_label,
                            read_only: Some(read_only),
                        },
                    )),
                })
            },
            PluginCommand::RevokeWebLoginToken(token_label) => Ok(ProtobufPluginCommand {
                name: CommandName::RevokeWebLoginToken as i32,
                payload: Some(Payload::RevokeWebLoginTokenPayload(
                    RevokeWebLoginTokenPayload { token_label },
                )),
            }),
            PluginCommand::ListWebLoginTokens => Ok(ProtobufPluginCommand {
                name: CommandName::ListWebLoginTokens as i32,
                payload: None,
            }),
            PluginCommand::RevokeAllWebLoginTokens => Ok(ProtobufPluginCommand {
                name: CommandName::RevokeAllWebLoginTokens as i32,
                payload: None,
            }),
            PluginCommand::RenameWebLoginToken(old_name, new_name) => Ok(ProtobufPluginCommand {
                name: CommandName::RenameWebLoginToken as i32,
                payload: Some(Payload::RenameWebLoginTokenPayload(
                    RenameWebLoginTokenPayload { old_name, new_name },
                )),
            }),
            PluginCommand::InterceptKeyPresses => Ok(ProtobufPluginCommand {
                name: CommandName::InterceptKeyPresses as i32,
                payload: None,
            }),
            PluginCommand::ClearKeyPressesIntercepts => Ok(ProtobufPluginCommand {
                name: CommandName::ClearKeyPressesIntercepts as i32,
                payload: None,
            }),
            PluginCommand::ReplacePaneWithExistingPane(
                pane_id_to_replace,
                existing_pane_id,
                suppress_replaced_pane,
            ) => Ok(ProtobufPluginCommand {
                name: CommandName::ReplacePaneWithExistingPane as i32,
                payload: Some(Payload::ReplacePaneWithExistingPanePayload(
                    ReplacePaneWithExistingPanePayload {
                        pane_id_to_replace: ProtobufPaneId::try_from(pane_id_to_replace).ok(),
                        existing_pane_id: ProtobufPaneId::try_from(existing_pane_id).ok(),
                        suppress_replaced_pane,
                    },
                )),
            }),
            PluginCommand::RunAction(action, context) => {
                let protobuf_action = ProtobufAction::try_from(action)
                    .map_err(|_| "Failed to convert action to protobuf")?;

                let context_items: Vec<ContextItem> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();

                Ok(ProtobufPluginCommand {
                    name: CommandName::RunAction as i32,
                    payload: Some(Payload::RunActionPayload(RunActionPayload {
                        action: Some(protobuf_action),
                        context: context_items,
                    })),
                })
            },
            PluginCommand::CopyToClipboard(text) => Ok(ProtobufPluginCommand {
                name: CommandName::CopyToClipboard as i32,
                payload: Some(Payload::CopyToClipboardPayload(CopyToClipboardPayload {
                    text,
                })),
            }),
            PluginCommand::GenerateRandomName => Ok(ProtobufPluginCommand {
                name: CommandName::GenerateRandomName as i32,
                payload: Some(Payload::GenerateRandomNamePayload(
                    GenerateRandomNamePayload {},
                )),
            }),
            PluginCommand::DumpLayout(layout_name) => Ok(ProtobufPluginCommand {
                name: CommandName::DumpLayout as i32,
                payload: Some(Payload::DumpLayoutPayload(DumpLayoutPayload {
                    layout_name,
                })),
            }),
            PluginCommand::ParseLayout(layout_string) => Ok(ProtobufPluginCommand {
                name: CommandName::ParseLayout as i32,
                payload: Some(Payload::ParseLayoutPayload(ParseLayoutPayload {
                    layout_string,
                })),
            }),
            PluginCommand::GetLayoutDir => Ok(ProtobufPluginCommand {
                name: CommandName::GetLayoutDir as i32,
                payload: Some(Payload::GetLayoutDirPayload(GetLayoutDirPayload {})),
            }),
            PluginCommand::GetFocusedPaneInfo => Ok(ProtobufPluginCommand {
                name: CommandName::GetFocusedPaneInfo as i32,
                payload: Some(Payload::GetFocusedPaneInfoPayload(
                    GetFocusedPaneInfoPayload {},
                )),
            }),
            PluginCommand::SaveSession => Ok(ProtobufPluginCommand {
                name: CommandName::SaveSession as i32,
                payload: Some(Payload::SaveSessionPayload(SaveSessionPayload {})),
            }),
            PluginCommand::CurrentSessionLastSavedTime => Ok(ProtobufPluginCommand {
                name: CommandName::CurrentSessionLastSavedTime as i32,
                payload: Some(Payload::CurrentSessionLastSavedTimePayload(
                    CurrentSessionLastSavedTimePayload {},
                )),
            }),
            PluginCommand::GetPaneInfo(pane_id) => {
                let protobuf_pane_id: ProtobufPaneId = pane_id.try_into()?;
                Ok(ProtobufPluginCommand {
                    name: CommandName::GetPaneInfo as i32,
                    payload: Some(Payload::GetPaneInfoPayload(GetPaneInfoPayload {
                        pane_id: Some(protobuf_pane_id),
                    })),
                })
            },
            PluginCommand::GetTabInfo(tab_id) => Ok(ProtobufPluginCommand {
                name: CommandName::GetTabInfo as i32,
                payload: Some(Payload::GetTabInfoPayload(GetTabInfoPayload {
                    tab_id: tab_id as u64,
                })),
            }),
            PluginCommand::GetSessionEnvironmentVariables => {
                let payload = ProtobufGetSessionEnvironmentVariablesPayload {};
                Ok(ProtobufPluginCommand {
                    name: CommandName::GetSessionEnvironmentVariables as i32,
                    payload: Some(Payload::GetSessionEnvironmentVariablesPayload(payload)),
                })
            },
            PluginCommand::OpenCommandPaneInNewTab(command_to_run, context) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenCommandPaneInNewTab as i32,
                    payload: Some(Payload::OpenCommandPaneInNewTabPayload(
                        OpenCommandPanePayload {
                            command_to_run: Some(command_to_run.try_into()?),
                            floating_pane_coordinates: None,
                            context,
                        },
                    )),
                })
            },
            PluginCommand::OpenPluginPaneInNewTab {
                plugin_url,
                configuration,
                context,
            } => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                let configuration: std::collections::HashMap<String, String> =
                    configuration.into_iter().collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenPluginPaneInNewTab as i32,
                    payload: Some(Payload::OpenPluginPaneInNewTabPayload(
                        ProtobufOpenPluginPaneInNewTabPayload {
                            plugin_url,
                            configuration,
                            context,
                        },
                    )),
                })
            },
            PluginCommand::OpenEditorPaneInNewTab(file_to_open, context) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenEditorPaneInNewTab as i32,
                    payload: Some(Payload::OpenEditorPaneInNewTabPayload(OpenFilePayload {
                        file_to_open: Some(file_to_open.try_into()?),
                        floating_pane_coordinates: None,
                        context,
                    })),
                })
            },
            PluginCommand::OpenCommandPaneInPlaceOfPaneId(
                pane_id,
                command_to_run,
                close_replaced_pane,
                context,
            ) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenCommandPaneInPlaceOfPaneId as i32,
                    payload: Some(Payload::OpenCommandPaneInPlaceOfPaneIdPayload(
                        OpenCommandPaneInPlaceOfPaneIdPayload {
                            pane_id_to_replace: Some(pane_id.try_into()?),
                            command_to_run: Some(command_to_run.try_into()?),
                            close_replaced_pane,
                            context,
                        },
                    )),
                })
            },
            PluginCommand::OpenTerminalPaneInPlaceOfPaneId(pane_id, cwd, close_replaced_pane) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenTerminalPaneInPlaceOfPaneId as i32,
                    payload: Some(Payload::OpenTerminalPaneInPlaceOfPaneIdPayload(
                        OpenTerminalPaneInPlaceOfPaneIdPayload {
                            pane_id_to_replace: Some(pane_id.try_into()?),
                            cwd: Some(cwd.try_into()?),
                            close_replaced_pane,
                        },
                    )),
                })
            },
            PluginCommand::OpenEditPaneInPlaceOfPaneId(
                pane_id,
                file_to_open,
                close_replaced_pane,
                context,
            ) => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenEditPaneInPlaceOfPaneId as i32,
                    payload: Some(Payload::OpenEditPaneInPlaceOfPaneIdPayload(
                        OpenEditPaneInPlaceOfPaneIdPayload {
                            pane_id_to_replace: Some(pane_id.try_into()?),
                            file_to_open: Some(file_to_open.try_into()?),
                            close_replaced_pane,
                            context,
                        },
                    )),
                })
            },
            PluginCommand::HideFloatingPanes { tab_id } => Ok(ProtobufPluginCommand {
                name: CommandName::HideFloatingPanes as i32,
                payload: Some(Payload::HideFloatingPanesPayload(
                    ProtobufHideFloatingPanesPayload {
                        tab_id: tab_id.map(|id| id as u32),
                    },
                )),
            }),
            PluginCommand::ShowFloatingPanes { tab_id } => Ok(ProtobufPluginCommand {
                name: CommandName::ShowFloatingPanes as i32,
                payload: Some(Payload::ShowFloatingPanesPayload(
                    ProtobufShowFloatingPanesPayload {
                        tab_id: tab_id.map(|id| id as u32),
                    },
                )),
            }),
            PluginCommand::SetPaneColor(pane_id, fg, bg) => Ok(ProtobufPluginCommand {
                name: CommandName::SetPaneColor as i32,
                payload: Some(Payload::SetPaneColorPayload(SetPaneColorPayload {
                    pane_id: Some(pane_id.try_into()?),
                    fg,
                    bg,
                })),
            }),
            PluginCommand::SetPaneRegexHighlights(pane_id, highlights) => {
                Ok(ProtobufPluginCommand {
                    name: CommandName::SetPaneRegexHighlights as i32,
                    payload: Some(Payload::SetPaneRegexHighlightsPayload(
                        SetPaneRegexHighlightsPayload {
                            pane_id: pane_id.try_into().ok(),
                            highlights: highlights
                                .into_iter()
                                .map(|h| {
                                    let style_variant = match h.style {
                                        HighlightStyle::Emphasis0 => {
                                            ProtobufHighlightStyleVariant::Emphasis0(true)
                                        },
                                        HighlightStyle::Emphasis1 => {
                                            ProtobufHighlightStyleVariant::Emphasis1(true)
                                        },
                                        HighlightStyle::Emphasis2 => {
                                            ProtobufHighlightStyleVariant::Emphasis2(true)
                                        },
                                        HighlightStyle::Emphasis3 => {
                                            ProtobufHighlightStyleVariant::Emphasis3(true)
                                        },
                                        HighlightStyle::CustomRgb { fg, bg } => {
                                            ProtobufHighlightStyleVariant::CustomRgb(
                                                ProtobufCustomRgbHighlight {
                                                    fg_r: fg.map(|(r, _, _)| r as u32),
                                                    fg_g: fg.map(|(_, g, _)| g as u32),
                                                    fg_b: fg.map(|(_, _, b)| b as u32),
                                                    bg_r: bg.map(|(r, _, _)| r as u32),
                                                    bg_g: bg.map(|(_, g, _)| g as u32),
                                                    bg_b: bg.map(|(_, _, b)| b as u32),
                                                },
                                            )
                                        },
                                        HighlightStyle::CustomIndex { fg, bg } => {
                                            ProtobufHighlightStyleVariant::CustomIndex(
                                                ProtobufCustomIndexHighlight {
                                                    fg: fg.map(|v| v as u32),
                                                    bg: bg.map(|v| v as u32),
                                                },
                                            )
                                        },
                                        HighlightStyle::BackgroundEmphasis0 => {
                                            ProtobufHighlightStyleVariant::BackgroundEmphasis0(true)
                                        },
                                        HighlightStyle::BackgroundEmphasis1 => {
                                            ProtobufHighlightStyleVariant::BackgroundEmphasis1(true)
                                        },
                                        HighlightStyle::BackgroundEmphasis2 => {
                                            ProtobufHighlightStyleVariant::BackgroundEmphasis2(true)
                                        },
                                        HighlightStyle::BackgroundEmphasis3 => {
                                            ProtobufHighlightStyleVariant::BackgroundEmphasis3(true)
                                        },
                                        HighlightStyle::None => {
                                            ProtobufHighlightStyleVariant::None(true)
                                        },
                                    };
                                    ProtobufRegexHighlight {
                                        pattern: h.pattern,
                                        style: Some(ProtobufHighlightStyle {
                                            style: Some(style_variant),
                                        }),
                                        context: h
                                            .context
                                            .into_iter()
                                            .map(|(name, value)| ContextItem { name, value })
                                            .collect(),
                                        on_hover: h.on_hover,
                                        bold: h.bold,
                                        italic: h.italic,
                                        underline: h.underline,
                                        tooltip_text: h.tooltip_text,
                                        layer: match h.layer {
                                            HighlightLayer::Hint => {
                                                ProtobufHighlightLayer::Hint as i32
                                            },
                                            HighlightLayer::Tool => {
                                                ProtobufHighlightLayer::Tool as i32
                                            },
                                            HighlightLayer::ActionFeedback => {
                                                ProtobufHighlightLayer::ActionFeedback as i32
                                            },
                                        },
                                    }
                                })
                                .collect(),
                        },
                    )),
                })
            },
            PluginCommand::ClearPaneHighlights(pane_id) => Ok(ProtobufPluginCommand {
                name: CommandName::ClearPaneHighlights as i32,
                payload: Some(Payload::ClearPaneHighlightsPayload(
                    ClearPaneHighlightsPayload {
                        pane_id: pane_id.try_into().ok(),
                    },
                )),
            }),
            PluginCommand::OpenPluginPaneFloating {
                plugin_url,
                configuration,
                floating_pane_coordinates,
                context,
            } => {
                let context: Vec<_> = context
                    .into_iter()
                    .map(|(name, value)| ContextItem { name, value })
                    .collect();
                let configuration: std::collections::HashMap<String, String> =
                    configuration.into_iter().collect();
                Ok(ProtobufPluginCommand {
                    name: CommandName::OpenPluginPaneFloating as i32,
                    payload: Some(Payload::OpenPluginPaneFloatingPayload(
                        OpenPluginPaneFloatingPayload {
                            plugin_url,
                            configuration,
                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
                            context,
                        },
                    )),
                })
            },
        }
    }
}

// Conversion implementations for tab creation response types
use crate::data::{
    BreakPanesToNewTabResponse, BreakPanesToTabWithIdResponse, BreakPanesToTabWithIndexResponse,
    FocusOrCreateTabResponse, NewTabResponse, NewTabUnfocusedResponse, NewTabsResponse,
    NewTiledPaneInTabResponse, OpenCommandPaneBackgroundResponse,
    OpenCommandPaneFloatingNearPluginResponse, OpenCommandPaneFloatingResponse,
    OpenCommandPaneInPlaceOfPaneIdResponse, OpenCommandPaneInPlaceOfPluginResponse,
    OpenCommandPaneInPlaceResponse, OpenCommandPaneNearPluginResponse, OpenCommandPaneResponse,
    OpenEditPaneInPlaceOfPaneIdResponse, OpenFileFloatingNearPluginResponse,
    OpenFileFloatingResponse, OpenFileInPlaceOfPluginResponse, OpenFileInPlaceResponse,
    OpenFileNearPluginResponse, OpenFileResponse, OpenPaneInNewTabResponse,
    OpenPluginPaneFloatingResponse, OpenTerminalFloatingNearPluginResponse,
    OpenTerminalFloatingResponse, OpenTerminalInPlaceOfPluginResponse, OpenTerminalInPlaceResponse,
    OpenTerminalNearPluginResponse, OpenTerminalPaneInPlaceOfPaneIdResponse, OpenTerminalResponse,
};

impl TryFrom<ProtobufNewTabResponse> for NewTabResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufNewTabResponse) -> Result<Self, Self::Error> {
        match protobuf.result {
            Some(new_tab_response::Result::TabId(id)) => Ok(Some(id as usize)),
            Some(new_tab_response::Result::None(_)) | None => Ok(None),
        }
    }
}

impl From<NewTabResponse> for ProtobufNewTabResponse {
    fn from(response: NewTabResponse) -> Self {
        match response {
            Some(tab_id) => ProtobufNewTabResponse {
                result: Some(new_tab_response::Result::TabId(tab_id as u64)),
            },
            None => ProtobufNewTabResponse {
                result: Some(new_tab_response::Result::None(true)),
            },
        }
    }
}

impl TryFrom<ProtobufNewTabUnfocusedResponse> for NewTabUnfocusedResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufNewTabUnfocusedResponse) -> Result<Self, Self::Error> {
        match protobuf.result {
            Some(new_tab_unfocused_response::Result::TabId(id)) => Ok(Some(id as usize)),
            Some(new_tab_unfocused_response::Result::None(_)) | None => Ok(None),
        }
    }
}

impl From<NewTabUnfocusedResponse> for ProtobufNewTabUnfocusedResponse {
    fn from(response: NewTabUnfocusedResponse) -> Self {
        match response {
            Some(tab_id) => ProtobufNewTabUnfocusedResponse {
                result: Some(new_tab_unfocused_response::Result::TabId(tab_id as u64)),
            },
            None => ProtobufNewTabUnfocusedResponse {
                result: Some(new_tab_unfocused_response::Result::None(true)),
            },
        }
    }
}

impl TryFrom<ProtobufNewTiledPaneInTabResponse> for NewTiledPaneInTabResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufNewTiledPaneInTabResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<NewTiledPaneInTabResponse> for ProtobufNewTiledPaneInTabResponse {
    fn from(response: NewTiledPaneInTabResponse) -> Self {
        ProtobufNewTiledPaneInTabResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufNewTabsResponse> for NewTabsResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufNewTabsResponse) -> Result<Self, Self::Error> {
        Ok(protobuf.tab_ids.into_iter().map(|id| id as usize).collect())
    }
}

impl From<NewTabsResponse> for ProtobufNewTabsResponse {
    fn from(response: NewTabsResponse) -> Self {
        ProtobufNewTabsResponse {
            tab_ids: response.into_iter().map(|id| id as u64).collect(),
        }
    }
}

impl TryFrom<ProtobufFocusOrCreateTabResponse> for FocusOrCreateTabResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufFocusOrCreateTabResponse) -> Result<Self, Self::Error> {
        match protobuf.result {
            Some(focus_or_create_tab_response::Result::TabId(id)) => Ok(Some(id as usize)),
            Some(focus_or_create_tab_response::Result::None(_)) | None => Ok(None),
        }
    }
}

impl From<FocusOrCreateTabResponse> for ProtobufFocusOrCreateTabResponse {
    fn from(response: FocusOrCreateTabResponse) -> Self {
        match response {
            Some(tab_id) => ProtobufFocusOrCreateTabResponse {
                result: Some(focus_or_create_tab_response::Result::TabId(tab_id as u64)),
            },
            None => ProtobufFocusOrCreateTabResponse {
                result: Some(focus_or_create_tab_response::Result::None(true)),
            },
        }
    }
}

impl TryFrom<ProtobufBreakPanesToNewTabResponse> for BreakPanesToNewTabResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufBreakPanesToNewTabResponse) -> Result<Self, Self::Error> {
        match protobuf.result {
            Some(break_panes_to_new_tab_response::Result::TabId(id)) => Ok(Some(id as usize)),
            Some(break_panes_to_new_tab_response::Result::None(_)) | None => Ok(None),
        }
    }
}

impl From<BreakPanesToNewTabResponse> for ProtobufBreakPanesToNewTabResponse {
    fn from(response: BreakPanesToNewTabResponse) -> Self {
        match response {
            Some(tab_id) => ProtobufBreakPanesToNewTabResponse {
                result: Some(break_panes_to_new_tab_response::Result::TabId(
                    tab_id as u64,
                )),
            },
            None => ProtobufBreakPanesToNewTabResponse {
                result: Some(break_panes_to_new_tab_response::Result::None(true)),
            },
        }
    }
}

impl TryFrom<ProtobufBreakPanesToTabWithIndexResponse> for BreakPanesToTabWithIndexResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufBreakPanesToTabWithIndexResponse) -> Result<Self, Self::Error> {
        match protobuf.result {
            Some(break_panes_to_tab_with_index_response::Result::TabId(id)) => {
                Ok(Some(id as usize))
            },
            Some(break_panes_to_tab_with_index_response::Result::None(_)) | None => Ok(None),
        }
    }
}

impl From<BreakPanesToTabWithIndexResponse> for ProtobufBreakPanesToTabWithIndexResponse {
    fn from(response: BreakPanesToTabWithIndexResponse) -> Self {
        match response {
            Some(tab_id) => ProtobufBreakPanesToTabWithIndexResponse {
                result: Some(break_panes_to_tab_with_index_response::Result::TabId(
                    tab_id as u64,
                )),
            },
            None => ProtobufBreakPanesToTabWithIndexResponse {
                result: Some(break_panes_to_tab_with_index_response::Result::None(true)),
            },
        }
    }
}

impl TryFrom<ProtobufBreakPanesToTabWithIdResponse> for BreakPanesToTabWithIdResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufBreakPanesToTabWithIdResponse) -> Result<Self, Self::Error> {
        match protobuf.result {
            Some(break_panes_to_tab_with_id_response::Result::TabId(id)) => Ok(Some(id as usize)),
            Some(break_panes_to_tab_with_id_response::Result::None(_)) | None => Ok(None),
        }
    }
}

impl From<BreakPanesToTabWithIdResponse> for ProtobufBreakPanesToTabWithIdResponse {
    fn from(response: BreakPanesToTabWithIdResponse) -> Self {
        match response {
            Some(tab_id) => ProtobufBreakPanesToTabWithIdResponse {
                result: Some(break_panes_to_tab_with_id_response::Result::TabId(
                    tab_id as u64,
                )),
            },
            None => ProtobufBreakPanesToTabWithIdResponse {
                result: Some(break_panes_to_tab_with_id_response::Result::None(true)),
            },
        }
    }
}

// Pane-creating command response conversions

impl TryFrom<ProtobufOpenFileResponse> for OpenFileResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenFileResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenFileResponse> for ProtobufOpenFileResponse {
    fn from(response: OpenFileResponse) -> Self {
        ProtobufOpenFileResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenFileFloatingResponse> for OpenFileFloatingResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenFileFloatingResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenFileFloatingResponse> for ProtobufOpenFileFloatingResponse {
    fn from(response: OpenFileFloatingResponse) -> Self {
        ProtobufOpenFileFloatingResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenFileInPlaceResponse> for OpenFileInPlaceResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenFileInPlaceResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenFileInPlaceResponse> for ProtobufOpenFileInPlaceResponse {
    fn from(response: OpenFileInPlaceResponse) -> Self {
        ProtobufOpenFileInPlaceResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenFileNearPluginResponse> for OpenFileNearPluginResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenFileNearPluginResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenFileNearPluginResponse> for ProtobufOpenFileNearPluginResponse {
    fn from(response: OpenFileNearPluginResponse) -> Self {
        ProtobufOpenFileNearPluginResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenFileFloatingNearPluginResponse> for OpenFileFloatingNearPluginResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenFileFloatingNearPluginResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenFileFloatingNearPluginResponse> for ProtobufOpenFileFloatingNearPluginResponse {
    fn from(response: OpenFileFloatingNearPluginResponse) -> Self {
        ProtobufOpenFileFloatingNearPluginResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenFileInPlaceOfPluginResponse> for OpenFileInPlaceOfPluginResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenFileInPlaceOfPluginResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenFileInPlaceOfPluginResponse> for ProtobufOpenFileInPlaceOfPluginResponse {
    fn from(response: OpenFileInPlaceOfPluginResponse) -> Self {
        ProtobufOpenFileInPlaceOfPluginResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenTerminalResponse> for OpenTerminalResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenTerminalResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenTerminalResponse> for ProtobufOpenTerminalResponse {
    fn from(response: OpenTerminalResponse) -> Self {
        ProtobufOpenTerminalResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenTerminalFloatingResponse> for OpenTerminalFloatingResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenTerminalFloatingResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenTerminalFloatingResponse> for ProtobufOpenTerminalFloatingResponse {
    fn from(response: OpenTerminalFloatingResponse) -> Self {
        ProtobufOpenTerminalFloatingResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenTerminalInPlaceResponse> for OpenTerminalInPlaceResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenTerminalInPlaceResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenTerminalInPlaceResponse> for ProtobufOpenTerminalInPlaceResponse {
    fn from(response: OpenTerminalInPlaceResponse) -> Self {
        ProtobufOpenTerminalInPlaceResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenTerminalNearPluginResponse> for OpenTerminalNearPluginResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenTerminalNearPluginResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenTerminalNearPluginResponse> for ProtobufOpenTerminalNearPluginResponse {
    fn from(response: OpenTerminalNearPluginResponse) -> Self {
        ProtobufOpenTerminalNearPluginResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenTerminalFloatingNearPluginResponse>
    for OpenTerminalFloatingNearPluginResponse
{
    type Error = &'static str;
    fn try_from(
        protobuf: ProtobufOpenTerminalFloatingNearPluginResponse,
    ) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenTerminalFloatingNearPluginResponse>
    for ProtobufOpenTerminalFloatingNearPluginResponse
{
    fn from(response: OpenTerminalFloatingNearPluginResponse) -> Self {
        ProtobufOpenTerminalFloatingNearPluginResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenTerminalInPlaceOfPluginResponse> for OpenTerminalInPlaceOfPluginResponse {
    type Error = &'static str;
    fn try_from(
        protobuf: ProtobufOpenTerminalInPlaceOfPluginResponse,
    ) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenTerminalInPlaceOfPluginResponse> for ProtobufOpenTerminalInPlaceOfPluginResponse {
    fn from(response: OpenTerminalInPlaceOfPluginResponse) -> Self {
        ProtobufOpenTerminalInPlaceOfPluginResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenCommandPaneResponse> for OpenCommandPaneResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenCommandPaneResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenCommandPaneResponse> for ProtobufOpenCommandPaneResponse {
    fn from(response: OpenCommandPaneResponse) -> Self {
        ProtobufOpenCommandPaneResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenCommandPaneFloatingResponse> for OpenCommandPaneFloatingResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenCommandPaneFloatingResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenCommandPaneFloatingResponse> for ProtobufOpenCommandPaneFloatingResponse {
    fn from(response: OpenCommandPaneFloatingResponse) -> Self {
        ProtobufOpenCommandPaneFloatingResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenCommandPaneInPlaceResponse> for OpenCommandPaneInPlaceResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenCommandPaneInPlaceResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenCommandPaneInPlaceResponse> for ProtobufOpenCommandPaneInPlaceResponse {
    fn from(response: OpenCommandPaneInPlaceResponse) -> Self {
        ProtobufOpenCommandPaneInPlaceResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenCommandPaneNearPluginResponse> for OpenCommandPaneNearPluginResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenCommandPaneNearPluginResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenCommandPaneNearPluginResponse> for ProtobufOpenCommandPaneNearPluginResponse {
    fn from(response: OpenCommandPaneNearPluginResponse) -> Self {
        ProtobufOpenCommandPaneNearPluginResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenCommandPaneFloatingNearPluginResponse>
    for OpenCommandPaneFloatingNearPluginResponse
{
    type Error = &'static str;
    fn try_from(
        protobuf: ProtobufOpenCommandPaneFloatingNearPluginResponse,
    ) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenCommandPaneFloatingNearPluginResponse>
    for ProtobufOpenCommandPaneFloatingNearPluginResponse
{
    fn from(response: OpenCommandPaneFloatingNearPluginResponse) -> Self {
        ProtobufOpenCommandPaneFloatingNearPluginResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenCommandPaneInPlaceOfPluginResponse>
    for OpenCommandPaneInPlaceOfPluginResponse
{
    type Error = &'static str;
    fn try_from(
        protobuf: ProtobufOpenCommandPaneInPlaceOfPluginResponse,
    ) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenCommandPaneInPlaceOfPluginResponse>
    for ProtobufOpenCommandPaneInPlaceOfPluginResponse
{
    fn from(response: OpenCommandPaneInPlaceOfPluginResponse) -> Self {
        ProtobufOpenCommandPaneInPlaceOfPluginResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenCommandPaneBackgroundResponse> for OpenCommandPaneBackgroundResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenCommandPaneBackgroundResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenCommandPaneBackgroundResponse> for ProtobufOpenCommandPaneBackgroundResponse {
    fn from(response: OpenCommandPaneBackgroundResponse) -> Self {
        ProtobufOpenCommandPaneBackgroundResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenPaneInNewTabResponse> for OpenPaneInNewTabResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenPaneInNewTabResponse) -> Result<Self, Self::Error> {
        let tab_id = protobuf.tab_id.map(|id| id as usize);
        let pane_id = match protobuf.pane_id {
            Some(pane_id) => Some(pane_id.try_into()?),
            None => None,
        };
        Ok(OpenPaneInNewTabResponse { tab_id, pane_id })
    }
}

impl From<OpenPaneInNewTabResponse> for ProtobufOpenPaneInNewTabResponse {
    fn from(response: OpenPaneInNewTabResponse) -> Self {
        ProtobufOpenPaneInNewTabResponse {
            tab_id: response.tab_id.map(|id| id as u64),
            pane_id: response.pane_id.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenCommandPaneInPlaceOfPaneIdResponse>
    for OpenCommandPaneInPlaceOfPaneIdResponse
{
    type Error = &'static str;
    fn try_from(
        protobuf: ProtobufOpenCommandPaneInPlaceOfPaneIdResponse,
    ) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenCommandPaneInPlaceOfPaneIdResponse>
    for ProtobufOpenCommandPaneInPlaceOfPaneIdResponse
{
    fn from(response: OpenCommandPaneInPlaceOfPaneIdResponse) -> Self {
        ProtobufOpenCommandPaneInPlaceOfPaneIdResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse>
    for OpenTerminalPaneInPlaceOfPaneIdResponse
{
    type Error = &'static str;
    fn try_from(
        protobuf: ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse,
    ) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenTerminalPaneInPlaceOfPaneIdResponse>
    for ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse
{
    fn from(response: OpenTerminalPaneInPlaceOfPaneIdResponse) -> Self {
        ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenEditPaneInPlaceOfPaneIdResponse> for OpenEditPaneInPlaceOfPaneIdResponse {
    type Error = &'static str;
    fn try_from(
        protobuf: ProtobufOpenEditPaneInPlaceOfPaneIdResponse,
    ) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenEditPaneInPlaceOfPaneIdResponse> for ProtobufOpenEditPaneInPlaceOfPaneIdResponse {
    fn from(response: OpenEditPaneInPlaceOfPaneIdResponse) -> Self {
        ProtobufOpenEditPaneInPlaceOfPaneIdResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

impl TryFrom<ProtobufOpenPluginPaneFloatingResponse> for OpenPluginPaneFloatingResponse {
    type Error = &'static str;
    fn try_from(protobuf: ProtobufOpenPluginPaneFloatingResponse) -> Result<Self, Self::Error> {
        match protobuf.pane_id {
            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
            None => Ok(None),
        }
    }
}

impl From<OpenPluginPaneFloatingResponse> for ProtobufOpenPluginPaneFloatingResponse {
    fn from(response: OpenPluginPaneFloatingResponse) -> Self {
        ProtobufOpenPluginPaneFloatingResponse {
            pane_id: response.map(|p| p.try_into().unwrap()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::data::PluginCommand;

    #[test]
    fn set_pane_frame_style_protobuf_round_trip() {
        use crate::input::options::PaneFrameStyle;
        for style in [
            PaneFrameStyle::Full,
            PaneFrameStyle::Titles,
            PaneFrameStyle::None,
        ] {
            let original = PluginCommand::SetPaneFrameStyle(style);
            let protobuf: ProtobufPluginCommand = original.try_into().expect("encode");
            let decoded: PluginCommand = protobuf.try_into().expect("decode");
            match decoded {
                PluginCommand::SetPaneFrameStyle(decoded_style) => {
                    assert_eq!(decoded_style, style)
                },
                other => panic!("expected SetPaneFrameStyle, got {:?}", other),
            }
        }
    }

    #[test]
    fn new_pane_protobuf_round_trip() {
        let original = PluginCommand::NewPane;
        let protobuf: ProtobufPluginCommand = original.try_into().expect("encode");
        let decoded: PluginCommand = protobuf.try_into().expect("decode");
        match decoded {
            PluginCommand::NewPane => {},
            other => panic!("expected NewPane, got {:?}", other),
        }
    }

    #[test]
    fn toggle_floating_panes_focused_tab_protobuf_round_trip() {
        let original = PluginCommand::ToggleFloatingPanes { tab_id: None };
        let protobuf: ProtobufPluginCommand = original.try_into().expect("encode");
        let decoded: PluginCommand = protobuf.try_into().expect("decode");
        match decoded {
            PluginCommand::ToggleFloatingPanes { tab_id } => assert_eq!(tab_id, None),
            other => panic!("expected ToggleFloatingPanes, got {:?}", other),
        }
    }

    #[test]
    fn toggle_floating_panes_with_tab_id_protobuf_round_trip() {
        let original = PluginCommand::ToggleFloatingPanes { tab_id: Some(2) };
        let protobuf: ProtobufPluginCommand = original.try_into().expect("encode");
        let decoded: PluginCommand = protobuf.try_into().expect("decode");
        match decoded {
            PluginCommand::ToggleFloatingPanes { tab_id } => assert_eq!(tab_id, Some(2)),
            other => panic!("expected ToggleFloatingPanes, got {:?}", other),
        }
    }
}
