52#include <wx/datetime.h>
58#include <wx/mstream.h>
59#include <wx/settings.h>
61#include <wx/stdpaths.h>
62#include <wx/webview.h>
66#include <nlohmann/json.hpp>
71 std::vector<uint8_t>& aOutput,
72 wxString& aError )
const
74 if( aEncoded.empty() )
76 aError =
_(
"Missing payload data." );
80 wxMemoryBuffer buffer = wxBase64Decode( wxString::FromUTF8( aEncoded.c_str() ) );
82 if( buffer.IsEmpty() )
84 aError =
_(
"Failed to decode base64 payload." );
88 aOutput.resize( buffer.GetDataLen() );
89 memcpy( aOutput.data(), buffer.GetData(), buffer.GetDataLen() );
95 const std::vector<uint8_t>& aInput,
96 std::vector<uint8_t>& aOutput,
97 wxString& aError )
const
99 if( aCompression.empty() || aCompression ==
"NONE" )
105 if( aCompression !=
"ZSTD" )
107 aError = wxString::Format(
_(
"Unsupported compression '%s'." ),
108 wxString::FromUTF8( aCompression ) );
114 aError =
_(
"Compressed payload was empty." );
118 unsigned long long expectedSize = ZSTD_getFrameContentSize( aInput.data(), aInput.size() );
120 if( expectedSize == ZSTD_CONTENTSIZE_ERROR || expectedSize == ZSTD_CONTENTSIZE_UNKNOWN )
121 expectedSize =
static_cast<unsigned long long>( aInput.size() ) * 4;
123 static constexpr unsigned long long FALLBACK_MAX = 64ULL * 1024 * 1024;
127 ?
static_cast<unsigned long long>(
131 if( expectedSize > maxBytes )
133 aError = wxString::Format(
_(
"Decompressed size %llu exceeds limit %llu." ),
134 expectedSize, maxBytes );
138 aOutput.resize( expectedSize );
140 size_t decompressed = ZSTD_decompress( aOutput.data(), expectedSize, aInput.data(), aInput.size() );
142 if( ZSTD_isError( decompressed ) )
144 aError = wxString::Format(
_(
"ZSTD decompression failed: %s" ),
145 wxString::FromUTF8( ZSTD_getErrorName( decompressed ) ) );
149 aOutput.resize( decompressed );
156 wxString script = wxString::FromUTF8( aJson.c_str() );
157 script.Replace(
"\\",
"\\\\" );
158 script.Replace(
"'",
"\\'" );
164 const wxString& aLibItemName,
165 wxString& aError )
const
167 if( aPayload.empty() )
169 aError =
_(
"Symbol payload was empty." );
173 wxString tempPath = wxFileName::CreateTempFileName( wxS(
"remote_symbol" ) );
175 if( tempPath.IsEmpty() )
177 aError =
_(
"Unable to create a temporary file for the symbol payload." );
181 wxFileName tempFile( tempPath );
182 wxFFile file( tempFile.GetFullPath(), wxS(
"wb" ) );
184 if( !file.IsOpened() )
186 aError =
_(
"Unable to create a temporary file for the symbol payload." );
187 wxRemoveFile( tempFile.GetFullPath() );
191 if( file.Write( aPayload.data(), aPayload.size() ) != aPayload.size() )
193 aError =
_(
"Failed to write the temporary symbol payload." );
195 wxRemoveFile( tempFile.GetFullPath() );
205 aError =
_(
"Unable to access the KiCad symbol plugin." );
206 wxRemoveFile( tempFile.GetFullPath() );
210 std::unique_ptr<LIB_SYMBOL> symbol;
214 LIB_SYMBOL* loaded = plugin->LoadSymbol( tempFile.GetFullPath(), aLibItemName );
217 symbol.reset( loaded );
219 aError =
_(
"Symbol payload did not include the expected symbol." );
223 aError = wxString::Format(
_(
"Unable to decode the symbol payload: %s" ), e.
What() );
226 wxRemoveFile( tempFile.GetFullPath() );
243 wxBoxSizer* topSizer =
new wxBoxSizer( wxVERTICAL );
244 wxBoxSizer* controlsSizer =
new wxBoxSizer( wxHORIZONTAL );
260 topSizer->Add( controlsSizer, 0, wxEXPAND | wxALL, FromDIP( 4 ) );
263 m_webView->AddMessageHandler( wxS(
"kicad" ),
264 [
this](
const wxString& aPayload )
268 m_webView->SetHandleExternalLinks(
true );
271 if( wxWebView* browser =
m_webView->GetWebView() )
274 topSizer->Add(
m_webView, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP( 2 ) );
276 SetSizer( topSizer );
304 wxFileName cookieFile( wxStandardPaths::Get().GetUserDataDir(), wxEmptyString );
305 cookieFile.AppendDir( wxS(
"remote-provider-cookies" ) );
306 cookieFile.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
317 if( wxWebView* browser =
m_webView->GetWebView() )
330 if( wxWebView* browser =
m_webView->GetWebView() )
334 if( cookieFile.FileExists() )
350 if( cookieFile.FileExists() )
351 wxRemoveFile( cookieFile.GetFullPath() );
372 wxString label = entry.display_name_override.IsEmpty() ? entry.metadata_url : entry.display_name_override;
393 selected =
static_cast<int>( ii );
412 wxUnusedVar( aEvent );
422 wxUnusedVar( aEvent );
461 settings->m_RemoteSymbol.last_used_provider_id =
m_providerEntries[aIndex].provider_id;
470 const wxString& aAccessToken )
488 const wxString& aAccessToken )
493 if( !
m_providerClient.ExchangeBootstrapNonce( aMetadata, aAccessToken, nonceUrl, error ) )
495 wxLogWarning(
"Session bootstrap nonce exchange failed: %s", error.
message );
509 wxColour bgColour = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
510 wxColour fgColour = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
513 html << wxS(
"<html><head><style>" )
514 << wxString::Format( wxS(
"body { background-color: #%02x%02x%02x; color: #%02x%02x%02x; "
515 "font-family: system-ui, sans-serif; padding: 10px; }" ),
516 bgColour.Red(), bgColour.Green(), bgColour.Blue(),
517 fgColour.Red(), fgColour.Green(), fgColour.Blue() )
518 << wxS(
"</style></head><body><p>" ) <<
EscapeHTML( aMessage )
519 << wxS(
"</p></body></html>" );
529 const wxString url = aEvent.GetURL();
531 if( !url.StartsWith( wxS(
"file://" ) ) )
553 nlohmann::json params = nlohmann::json::object();
554 params[
"client_name"] =
"KiCad";
566 const wxString script = wxString::Format( wxS(
"window.kiclient.postMessage('%s');" ),
573 nlohmann::json aParameters )
575 nlohmann::json envelope = nlohmann::json::object();
577 envelope[
"session_id"] =
m_sessionId.AsStdString();
579 envelope[
"command"] = aCommand.ToStdString();
580 envelope[
"status"] =
"OK";
581 envelope[
"response_to"] = aResponseTo;
583 if( !aParameters.is_null() && !aParameters.empty() )
584 envelope[
"parameters"] = std::move( aParameters );
591 const wxString& aErrorCode,
const wxString& aErrorMessage )
593 nlohmann::json envelope = nlohmann::json::object();
595 envelope[
"session_id"] =
m_sessionId.AsStdString();
597 envelope[
"command"] = aCommand.ToStdString();
598 envelope[
"status"] =
"ERROR";
599 envelope[
"response_to"] = aResponseTo;
600 envelope[
"error_code"] = aErrorCode.ToStdString();
601 envelope[
"error_message"] = aErrorMessage.ToStdString();
608 nlohmann::json envelope = nlohmann::json::object();
610 envelope[
"session_id"] =
m_sessionId.AsStdString();
612 envelope[
"command"] = aCommand.ToStdString();
613 envelope[
"status"] =
"OK";
615 if( !aParameters.is_null() && !aParameters.empty() )
616 envelope[
"parameters"] = std::move( aParameters );
624 wxScopedCharBuffer utf8 = aMessage.ToUTF8();
626 if( !utf8 || utf8.length() == 0 )
633 catch(
const std::exception& e )
635 wxLogWarning(
"Remote symbol RPC parse error: %s", e.what() );
648 std::optional<OAUTH_TOKEN_SET> tokens =
loadTokens( aProvider );
653 const long long now =
static_cast<long long>( wxDateTime::Now().GetTicks() );
655 if( tokens->expires_at == 0 || tokens->expires_at > now + 60 )
656 return tokens->access_token;
670 tokens->refresh_token, refreshed, error ) )
696 aError =
_(
"A remote provider sign-in flow is already in progress." );
712 wxArrayString scopes;
714 for(
const wxString& scope : aMetadata.
auth.
scopes )
728 aError =
_(
"Unable to start the local OAuth callback listener." );
738 aError =
_(
"Unable to open the system browser for sign-in." );
750 if( std::optional<OAUTH_TOKEN_SET> tokens =
loadTokens( aProvider ); tokens )
758 const wxString tokenToRevoke = !tokens->refresh_token.IsEmpty() ? tokens->refresh_token
759 : tokens->access_token;
762 tokenToRevoke, revokeError );
768 aError =
_(
"Failed to delete stored remote provider tokens." );
778 provider->last_account_label.clear();
779 provider->last_auth_status = wxS(
"signed_out" );
789 if( !aMessage.is_object() )
794 if( command.IsEmpty() )
797 auto messageIdIt = aMessage.find(
"message_id" );
799 if( messageIdIt == aMessage.end() || !messageIdIt->is_number_integer() )
802 const int messageId = messageIdIt->get<
int>();
803 const int version = aMessage.value(
"version", 0 );
807 sendRpcError( command, messageId, wxS(
"UNSUPPORTED_VERSION" ),
808 wxString::Format(
_(
"Unsupported RPC version %d." ), version ) );
814 if( sessionId.IsEmpty() )
816 sendRpcError( command, messageId, wxS(
"INVALID_PARAMETERS" ),
817 _(
"Missing session identifier." ) );
821 if( !sessionId.IsSameAs(
m_sessionId.AsString() ) )
823 sendRpcError( command, messageId, wxS(
"SESSION_MISMATCH" ),
824 _(
"Session identifier did not match the active provider session." ) );
828 nlohmann::json params = nlohmann::json::object();
829 auto paramsIt = aMessage.find(
"parameters" );
831 if( paramsIt != aMessage.end() && paramsIt->is_object() )
834 const std::string data = aMessage.value(
"data", std::string() );
836 if( command == wxS(
"NEW_SESSION" ) )
838 nlohmann::json reply = nlohmann::json::object();
839 reply[
"client_name"] =
"KiCad";
846 if( command == wxS(
"GET_KICAD_VERSION" ) )
848 nlohmann::json reply = nlohmann::json::object();
854 if( command == wxS(
"LIST_SUPPORTED_VERSIONS" ) )
856 nlohmann::json reply = nlohmann::json::object();
862 if( command == wxS(
"CAPABILITIES" ) )
864 nlohmann::json reply = nlohmann::json::object();
865 reply[
"commands"] = {
"NEW_SESSION",
"GET_KICAD_VERSION",
"LIST_SUPPORTED_VERSIONS",
866 "CAPABILITIES",
"GET_SOURCE_INFO",
"REMOTE_LOGIN",
"DL_SYMBOL",
867 "DL_COMPONENT",
"DL_FOOTPRINT",
"DL_SPICE",
"DL_3DMODEL",
869 reply[
"compression"] = {
"NONE",
"ZSTD" };
870 reply[
"max_message_size"] = 0;
875 if( command == wxS(
"GET_SOURCE_INFO" ) )
877 nlohmann::json reply = nlohmann::json::object();
888 bool authenticated =
false;
893 reply[
"authenticated"] = authenticated;
899 reply[
"supports_direct_downloads"] =
901 reply[
"supports_inline_payloads"] =
907 if( command == wxS(
"REMOTE_LOGIN" ) )
912 _(
"No remote provider is currently selected." ) );
917 const bool signOut = params.value(
"sign_out",
false );
918 const bool interactive = params.value(
"interactive",
true );
926 sendRpcError( command, messageId, wxS(
"SIGN_OUT_FAILED" ), error );
932 nlohmann::json reply = nlohmann::json::object();
933 reply[
"authenticated"] =
false;
934 reply[
"signed_out"] =
true;
941 if( !accessToken.IsEmpty() )
943 nlohmann::json reply = nlohmann::json::object();
944 reply[
"authenticated"] =
true;
945 reply[
"provider_id"] = provider.
provider_id.ToStdString();
952 nlohmann::json reply = nlohmann::json::object();
953 reply[
"authenticated"] =
false;
954 reply[
"auth_type"] =
"none";
961 nlohmann::json reply = nlohmann::json::object();
962 reply[
"authenticated"] =
false;
963 reply[
"started"] =
false;
972 sendRpcError( command, messageId, wxS(
"LOGIN_FAILED" ), error );
976 nlohmann::json reply = nlohmann::json::object();
977 reply[
"authenticated"] =
false;
978 reply[
"started"] =
true;
983 if( command == wxS(
"PLACE_COMPONENT" ) || command == wxS(
"DL_COMPONENT" ) || command == wxS(
"DL_SYMBOL" )
984 || command == wxS(
"DL_FOOTPRINT" ) || command == wxS(
"DL_3DMODEL" ) || command == wxS(
"DL_SPICE" ) )
986 const bool placeSymbol = command == wxS(
"PLACE_COMPONENT" )
988 const bool isComponent = command == wxS(
"DL_COMPONENT" ) || command == wxS(
"PLACE_COMPONENT" );
992 if( isComponent && params.contains(
"assets" ) && params[
"assets"].is_array() )
999 std::vector<uint8_t> decoded;
1003 sendRpcError( command, messageId, wxS(
"INVALID_PAYLOAD" ), error );
1007 std::vector<uint8_t> payload;
1008 const std::string compressionStr =
1009 compression.IsEmpty() ? std::string() : std::string( compression.ToUTF8().data() );
1013 sendRpcError( command, messageId, wxS(
"INVALID_PAYLOAD" ), error );
1019 else if( command == wxS(
"DL_SYMBOL" ) )
1021 else if( command == wxS(
"DL_FOOTPRINT" ) )
1023 else if( command == wxS(
"DL_3DMODEL" ) )
1025 else if( command == wxS(
"DL_SPICE" ) )
1032 sendRpcError( command, messageId, wxS(
"IMPORT_FAILED" ),
1033 error.IsEmpty() ?
_(
"Unable to process provider payload." ) : error );
1038 sendRpcError( command, messageId, wxS(
"UNKNOWN_COMMAND" ),
1039 wxString::Format(
_(
"Command '%s' is not supported." ), command ) );
1044 const std::vector<uint8_t>& aPayload,
1048 const bool placeAfterDownload = mode.IsSameAs( wxS(
"PLACE" ),
false );
1050 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false ) && !placeAfterDownload )
1052 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1058 aError =
_(
"Unsupported symbol payload type." );
1064 aError =
_(
"No schematic editor is available to store symbols." );
1072 aError =
_(
"Unable to load schematic settings." );
1081 wxFileName symDir = baseDir;
1082 symDir.AppendDir( wxS(
"symbols" ) );
1083 symDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1087 if( libItemName.IsEmpty() )
1088 libItemName = wxS(
"symbol" );
1092 if( libraryName.IsEmpty() )
1093 libraryName = wxS(
"symbols" );
1098 wxFileName outFile( symDir );
1099 outFile.SetFullName( nickname + wxS(
".kicad_sym" ) );
1109 aError =
_(
"Unable to access the symbol library manager." );
1113 std::unique_ptr<LIB_SYMBOL> downloadedSymbol =
loadSymbolFromPayload( aPayload, libItemName, aError );
1115 if( !downloadedSymbol )
1118 downloadedSymbol->SetName( libItemName );
1122 downloadedSymbol->SetLibId( savedId );
1126 aError =
_(
"Unable to save the downloaded symbol." );
1135 if( placeAfterDownload )
1143 const std::vector<uint8_t>& aPayload,
1144 bool aPlaceSymbol, wxString& aError )
1146 nlohmann::json components;
1150 components = nlohmann::json::parse( aPayload.begin(), aPayload.end() );
1152 catch(
const std::exception& e )
1154 aError = wxString::Format(
_(
"Failed to parse component list: %s" ), e.what() );
1158 if( !components.is_array() || components.empty() )
1160 aError =
_(
"Component list must be a non-empty array." );
1166 for(
const nlohmann::json& entry : components )
1168 if( !entry.is_object() )
1170 aError =
_(
"Component entries must be objects." );
1174 std::string entryType = entry.value(
"type",
"" );
1176 if( entryType.empty() )
1178 aError =
_(
"Component entry was missing a type." );
1182 std::transform( entryType.begin(), entryType.end(), entryType.begin(),
1183 [](
unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
1185 std::vector<uint8_t> content;
1190 const std::string compression = entry.value(
"compression", std::string() );
1192 if( !compression.empty() && compression !=
"NONE" )
1194 std::vector<uint8_t> decoded = content;
1200 wxString entryName = wxString::FromUTF8( entry.value(
"name",
"" ) );
1201 nlohmann::json entryParams = nlohmann::json::object();
1203 if( !libraryName.IsEmpty() )
1204 entryParams[
"library"] = libraryName.ToStdString();
1206 if( !entryName.IsEmpty() )
1207 entryParams[
"name"] = entryName.ToStdString();
1211 if( entryType ==
"symbol" )
1213 entryParams[
"content_type"] =
"KICAD_SYMBOL_V1";
1214 entryParams[
"mode"] = aPlaceSymbol ?
"PLACE" :
"SAVE";
1217 else if( entryType ==
"footprint" )
1219 entryParams[
"content_type"] =
"KICAD_FOOTPRINT_V1";
1220 entryParams[
"mode"] =
"SAVE";
1223 else if( entryType ==
"3dmodel" )
1225 entryParams[
"content_type"] =
"KICAD_3D_MODEL_STEP";
1226 entryParams[
"mode"] =
"SAVE";
1229 else if( entryType ==
"spice" )
1231 entryParams[
"content_type"] =
"KICAD_SPICE_MODEL_V1";
1232 entryParams[
"mode"] =
"SAVE";
1237 aError = wxString::Format(
_(
"Unsupported component type '%s'." ),
1238 wxString::FromUTF8( entryType.c_str() ) );
1251 const std::vector<uint8_t>& aPayload,
1256 aError =
_(
"Unsupported footprint payload type." );
1265 wxFileName fpRoot = baseDir;
1266 fpRoot.AppendDir( wxS(
"footprints" ) );
1267 fpRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1272 if( libraryName.IsEmpty() )
1273 libraryName = wxS(
"footprints" );
1278 wxFileName libDir = fpRoot;
1279 libDir.AppendDir( libNickname + wxS(
".pretty" ) );
1280 libDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1282 if( !footprintName.Lower().EndsWith( wxS(
".kicad_mod" ) ) )
1283 footprintName += wxS(
".kicad_mod" );
1285 wxFileName outFile( libDir );
1286 outFile.SetFullName( footprintName );
1295 aError =
_(
"Unable to load schematic settings." );
1312 const std::vector<uint8_t>& aPayload,
1320 wxFileName modelDir = baseDir;
1322 modelDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1326 if( fileName.IsEmpty() )
1331 wxFileName outFile( modelDir );
1332 outFile.SetFullName( fileName );
1338 const std::vector<uint8_t>& aPayload,
1346 wxFileName spiceDir = baseDir;
1348 spiceDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1352 if( fileName.IsEmpty() )
1357 if( !fileName.Lower().EndsWith( wxS(
".cir" ) ) )
1358 fileName += wxS(
".cir" );
1360 wxFileName outFile( spiceDir );
1361 outFile.SetFullName( fileName );
1367 bool aPlaceSymbol, wxString& aError )
1371 aError =
_(
"Remote provider metadata is not loaded." );
1381 for(
const nlohmann::json& assetJson : aParams.at(
"assets" ) )
1391 asset.
required = assetJson.value(
"required",
false );
1393 auto sizeIt = assetJson.find(
"size_bytes" );
1395 if( sizeIt != assetJson.end() && sizeIt->is_number_integer() )
1401 aError =
_(
"Manifest assets require asset_type, content_type, size_bytes, and download_url." );
1405 manifest.
assets.push_back( asset );
1437 showMessage(
_(
"Remote provider sign-in was cancelled or failed." ) );
1445 aEvent.GetString(), tokens, error ) )
1464 showMessage(
_(
"Failed to store remote provider tokens securely." ) );
1472 provider->last_account_label = wxS(
"default" );
1473 provider->last_auth_status = wxS(
"signed_in" );
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
wxString GetSemanticVersion()
Get the semantic version string for KiCad defined inside the KiCadVersion.cmake file in the variable ...
REMOTE_PROVIDER_SETTINGS m_RemoteSymbol
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
void ReloadLibraryEntry(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH)
A logical library item identifier and consists of various portions much like a URI.
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Define a library symbol object.
static wxString GenerateCodeVerifier()
static wxString GenerateState()
bool receiveSymbol(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, wxString &aError)
void onWebViewLoaded(wxWebViewEvent &aEvent)
void onKicadMessage(const wxString &aMessage)
void showMessage(const wxString &aMessage)
wxString m_pendingProviderId
void handleRpcMessage(const nlohmann::json &aMessage)
wxFileName cookieFilePath(const wxString &aProviderId) const
void loadProviderPage(const REMOTE_PROVIDER_METADATA &aMetadata, const wxString &aAccessToken)
bool receiveFootprint(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, wxString &aError)
void sendRpcError(const wxString &aCommand, int aResponseTo, const wxString &aErrorCode, const wxString &aErrorMessage)
int m_selectedProviderIndex
bool loadProvider(int aIndex)
bool signOutProvider(const REMOTE_PROVIDER_ENTRY &aProvider, wxString &aError)
OAUTH_SESSION m_pendingOAuthSession
bool receiveComponentManifest(const nlohmann::json &aParams, bool aPlaceSymbol, wxString &aError)
void sendRpcEnvelope(nlohmann::json aEnvelope)
bool m_hasSelectedProviderMetadata
PANEL_REMOTE_SYMBOL(SCH_EDIT_FRAME *aParent)
wxChoice * m_dataSourceChoice
bool decompressIfNeeded(const std::string &aCompression, const std::vector< uint8_t > &aInput, std::vector< uint8_t > &aOutput, wxString &aError) const
REMOTE_PROVIDER_METADATA m_selectedProviderMetadata
bool decodeBase64Payload(const std::string &aMessage, std::vector< uint8_t > &aOutPayload, wxString &aError) const
void RefreshDataSources()
void clearCookies(bool aDeleteSavedCookieFile=true)
wxString sanitizeForScript(const std::string &aJson) const
void beginSessionHandshake()
void onOAuthLoopback(wxCommandEvent &aEvent)
std::unique_ptr< LIB_SYMBOL > loadSymbolFromPayload(const std::vector< uint8_t > &aPayload, const wxString &aLibItemName, wxString &aError) const
void onConfigure(wxCommandEvent &aEvent)
bool receiveComponent(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, bool aPlaceSymbol, wxString &aError)
void sendRpcReply(const wxString &aCommand, int aResponseTo, nlohmann::json aParameters=nlohmann::json::object())
bool startInteractiveLogin(const REMOTE_PROVIDER_ENTRY &aProvider, const REMOTE_PROVIDER_METADATA &aMetadata, wxString &aError)
std::vector< REMOTE_PROVIDER_ENTRY > m_providerEntries
wxString loadAccessToken(const REMOTE_PROVIDER_ENTRY &aProvider)
void sendRpcNotification(const wxString &aCommand, nlohmann::json aParameters=nlohmann::json::object())
bool receiveSPICEModel(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, wxString &aError)
BITMAP_BUTTON * m_configButton
void onDarkModeToggle(wxSysColourChangedEvent &aEvent)
REMOTE_PROVIDER_CLIENT m_providerClient
SECURE_TOKEN_STORE m_tokenStore
WEBVIEW_PANEL * m_webView
bool receive3DModel(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, wxString &aError)
BITMAP_BUTTON * m_refreshButton
~PANEL_REMOTE_SYMBOL() override
void onRefresh(wxCommandEvent &aEvent)
void bootstrapAuthenticatedSession(const REMOTE_PROVIDER_METADATA &aMetadata, const wxString &aAccessToken)
REMOTE_PROVIDER_OAUTH_SERVER_METADATA m_pendingOAuthMetadata
std::unique_ptr< OAUTH_LOOPBACK_SERVER > m_oauthLoopbackServer
std::optional< OAUTH_TOKEN_SET > loadTokens(const REMOTE_PROVIDER_ENTRY &aProvider) const
void onDataSourceChanged(wxCommandEvent &aEvent)
virtual LIBRARY_MANAGER & GetLibraryManager() const
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
bool Import(const REMOTE_PROVIDER_METADATA &aProvider, const REMOTE_SYMBOL_IMPORT_CONTEXT &aContext, const REMOTE_PROVIDER_PART_MANIFEST &aManifest, bool aPlaceSymbol, wxString &aError)
Schematic editor (Eeschema) main window.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
SAVE_T SaveSymbol(const wxString &aNickname, const LIB_SYMBOL *aSymbol, bool aOverwrite=true)
Write aSymbol to an existing library given by aNickname.
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
#define REMOTE_SYMBOL_SESSION_VERSION
PGM_BASE & Pgm()
The global program "get" accessor.
wxString RemoteProviderJsonString(const nlohmann::json &aObject, const char *aKey)
Extract an optional string value from a JSON object, returning an empty wxString when the key is abse...
bool PlaceRemoteDownloadedSymbol(SCH_EDIT_FRAME *aFrame, const wxString &aNickname, const wxString &aLibItemName, wxString &aError)
Place a symbol from a remote download into the schematic editor.
bool EnsureRemoteLibraryEntry(LIBRARY_TABLE_TYPE aTableType, const wxFileName &aLibraryPath, const wxString &aNickname, bool aGlobalTable, bool aStrict, wxString &aError)
Add or update a library table entry for a remote download library.
bool WriteRemoteBinaryFile(const wxFileName &aOutput, const std::vector< uint8_t > &aPayload, wxString &aError)
Write binary data to a file, creating parent directories as needed.
bool EnsureRemoteDestinationRoot(wxFileName &aOutDir, wxString &aError)
Resolve and create the configured destination root directory for remote symbol downloads.
wxString RemoteLibraryPrefix()
Return the configured (or default) library prefix for remote downloads, sanitized for use as a filena...
wxString SanitizeRemoteFileComponent(const wxString &aValue, const wxString &aDefault, bool aLower)
Replace non-alphanumeric characters (other than _ - .) with underscores.
T * GetAppSettings(const char *aFilename)
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
std::vector< REMOTE_PROVIDER_PART_ASSET > assets
std::vector< REMOTE_PROVIDER_ENTRY > providers
wxString last_used_provider_id