51#include <wx/datetime.h>
57#include <wx/mstream.h>
58#include <wx/settings.h>
60#include <wx/stdpaths.h>
61#include <wx/webview.h>
65#include <nlohmann/json.hpp>
70 std::vector<uint8_t>& aOutput,
71 wxString& aError )
const
73 if( aEncoded.empty() )
75 aError =
_(
"Missing payload data." );
79 wxMemoryBuffer buffer = wxBase64Decode( wxString::FromUTF8( aEncoded.c_str() ) );
81 if( buffer.IsEmpty() )
83 aError =
_(
"Failed to decode base64 payload." );
87 aOutput.resize( buffer.GetDataLen() );
88 memcpy( aOutput.data(), buffer.GetData(), buffer.GetDataLen() );
94 std::vector<uint8_t>& aOutput, wxString& aError )
const
96 if( aCompression.empty() || aCompression ==
"NONE" )
102 if( aCompression !=
"ZSTD" )
104 aError = wxString::Format(
_(
"Unsupported compression '%s'." ), wxString::FromUTF8( aCompression ) );
110 aError =
_(
"Compressed payload was empty." );
114 unsigned long long expectedSize = ZSTD_getFrameContentSize( aInput.data(), aInput.size() );
116 if( expectedSize == ZSTD_CONTENTSIZE_ERROR || expectedSize == ZSTD_CONTENTSIZE_UNKNOWN )
117 expectedSize =
static_cast<unsigned long long>( aInput.size() ) * 4;
119 static constexpr unsigned long long FALLBACK_MAX = 64ULL * 1024 * 1024;
123 ?
static_cast<unsigned long long>(
127 if( expectedSize > maxBytes )
129 aError = wxString::Format(
_(
"Decompressed size %llu exceeds limit %llu." ),
130 expectedSize, maxBytes );
134 aOutput.resize( expectedSize );
136 size_t decompressed = ZSTD_decompress( aOutput.data(), expectedSize, aInput.data(), aInput.size() );
138 if( ZSTD_isError( decompressed ) )
140 aError = wxString::Format(
_(
"ZSTD decompression failed: %s" ),
141 wxString::FromUTF8( ZSTD_getErrorName( decompressed ) ) );
145 aOutput.resize( decompressed );
152 wxString script = wxString::FromUTF8( aJson.c_str() );
153 script.Replace(
"\\",
"\\\\" );
154 script.Replace(
"'",
"\\'" );
171 wxBoxSizer* topSizer =
new wxBoxSizer( wxVERTICAL );
172 wxBoxSizer* controlsSizer =
new wxBoxSizer( wxHORIZONTAL );
188 topSizer->Add( controlsSizer, 0, wxEXPAND | wxALL, FromDIP( 4 ) );
189 SetSizer( topSizer );
221 m_webView->AddMessageHandler( wxS(
"kicad" ),
222 [
this](
const wxString& aPayload )
226 m_webView->SetHandleExternalLinks(
true );
229 if( wxWebView* browser =
m_webView->GetWebView() )
232 GetSizer()->Add(
m_webView, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP( 2 ) );
246 wxFileName cookieFile( wxStandardPaths::Get().GetUserDataDir(), wxEmptyString );
247 cookieFile.AppendDir( wxS(
"remote-provider-cookies" ) );
248 cookieFile.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
263 if( wxWebView* browser =
m_webView->GetWebView() )
280 if( wxWebView* browser =
m_webView->GetWebView() )
284 if( cookieFile.FileExists() )
295 if( aDeleteSavedCookieFile
301 if( cookieFile.FileExists() )
302 wxRemoveFile( cookieFile.GetFullPath() );
323 wxString label = entry.display_name_override.IsEmpty() ? entry.metadata_url : entry.display_name_override;
347 selected =
static_cast<int>( ii );
368 wxUnusedVar( aEvent );
378 wxUnusedVar( aEvent );
417 settings->m_RemoteSymbol.last_used_provider_id =
m_providerEntries[aIndex].provider_id;
443 const wxString& aAccessToken )
448 if( !
m_providerClient.ExchangeBootstrapNonce( aMetadata, aAccessToken, nonceUrl, error ) )
450 wxLogWarning(
"Session bootstrap nonce exchange failed: %s", error.
message );
464 wxColour bgColour = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
465 wxColour fgColour = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
468 html << wxS(
"<html><head><style>" )
469 << wxString::Format( wxS(
"body { background-color: #%02x%02x%02x; color: #%02x%02x%02x; "
470 "font-family: system-ui, sans-serif; padding: 10px; }" ),
471 bgColour.Red(), bgColour.Green(), bgColour.Blue(),
472 fgColour.Red(), fgColour.Green(), fgColour.Blue() )
473 << wxS(
"</style></head><body><p>" ) <<
EscapeHTML( aMessage )
474 << wxS(
"</p></body></html>" );
484 const wxString url = aEvent.GetURL();
486 if( !url.StartsWith( wxS(
"file://" ) ) )
508 nlohmann::json params = nlohmann::json::object();
509 params[
"client_name"] =
"KiCad";
521 const wxString script = wxString::Format( wxS(
"window.kiclient.postMessage('%s');" ),
528 nlohmann::json aParameters )
530 nlohmann::json envelope = nlohmann::json::object();
532 envelope[
"session_id"] =
m_sessionId.AsStdString();
534 envelope[
"command"] = aCommand.ToStdString();
535 envelope[
"status"] =
"OK";
536 envelope[
"response_to"] = aResponseTo;
538 if( !aParameters.is_null() && !aParameters.empty() )
539 envelope[
"parameters"] = std::move( aParameters );
546 const wxString& aErrorCode,
const wxString& aErrorMessage )
548 nlohmann::json envelope = nlohmann::json::object();
550 envelope[
"session_id"] =
m_sessionId.AsStdString();
552 envelope[
"command"] = aCommand.ToStdString();
553 envelope[
"status"] =
"ERROR";
554 envelope[
"response_to"] = aResponseTo;
555 envelope[
"error_code"] = aErrorCode.ToStdString();
556 envelope[
"error_message"] = aErrorMessage.ToStdString();
563 nlohmann::json envelope = nlohmann::json::object();
565 envelope[
"session_id"] =
m_sessionId.AsStdString();
567 envelope[
"command"] = aCommand.ToStdString();
568 envelope[
"status"] =
"OK";
570 if( !aParameters.is_null() && !aParameters.empty() )
571 envelope[
"parameters"] = std::move( aParameters );
579 wxScopedCharBuffer utf8 = aMessage.ToUTF8();
581 if( !utf8 || utf8.length() == 0 )
588 catch(
const std::exception& e )
590 wxLogWarning(
"Remote symbol RPC parse error: %s", e.what() );
603 std::optional<OAUTH_TOKEN_SET> tokens =
loadTokens( aProvider );
608 const long long now =
static_cast<long long>( wxDateTime::Now().GetTicks() );
610 if( tokens->expires_at == 0 || tokens->expires_at > now + 60 )
611 return tokens->access_token;
625 tokens->refresh_token, refreshed, error ) )
651 aError =
_(
"A remote provider sign-in flow is already in progress." );
667 wxArrayString scopes;
669 for(
const wxString& scope : aMetadata.
auth.
scopes )
683 aError =
_(
"Unable to start the local OAuth callback listener." );
693 aError =
_(
"Unable to open the system browser for sign-in." );
705 if( std::optional<OAUTH_TOKEN_SET> tokens =
loadTokens( aProvider ); tokens )
713 const wxString tokenToRevoke = !tokens->refresh_token.IsEmpty() ? tokens->refresh_token
714 : tokens->access_token;
717 tokenToRevoke, revokeError );
723 aError =
_(
"Failed to delete stored remote provider tokens." );
733 provider->last_account_label.clear();
734 provider->last_auth_status = wxS(
"signed_out" );
744 if( !aMessage.is_object() )
749 if( command.IsEmpty() )
752 auto messageIdIt = aMessage.find(
"message_id" );
754 if( messageIdIt == aMessage.end() || !messageIdIt->is_number_integer() )
757 const int messageId = messageIdIt->get<
int>();
758 const int version = aMessage.value(
"version", 0 );
762 sendRpcError( command, messageId, wxS(
"UNSUPPORTED_VERSION" ),
763 wxString::Format(
_(
"Unsupported RPC version %d." ), version ) );
769 if( sessionId.IsEmpty() )
771 sendRpcError( command, messageId, wxS(
"INVALID_PARAMETERS" ),
_(
"Missing session identifier." ) );
775 if( !sessionId.IsSameAs(
m_sessionId.AsString() ) )
777 sendRpcError( command, messageId, wxS(
"SESSION_MISMATCH" ),
778 _(
"Session identifier did not match the active provider session." ) );
782 nlohmann::json params = nlohmann::json::object();
783 auto paramsIt = aMessage.find(
"parameters" );
785 if( paramsIt != aMessage.end() && paramsIt->is_object() )
788 const std::string data = aMessage.value(
"data", std::string() );
790 if( command == wxS(
"NEW_SESSION" ) )
792 nlohmann::json reply = nlohmann::json::object();
793 reply[
"client_name"] =
"KiCad";
800 if( command == wxS(
"GET_KICAD_VERSION" ) )
802 nlohmann::json reply = nlohmann::json::object();
808 if( command == wxS(
"LIST_SUPPORTED_VERSIONS" ) )
810 nlohmann::json reply = nlohmann::json::object();
816 if( command == wxS(
"CAPABILITIES" ) )
818 nlohmann::json reply = nlohmann::json::object();
819 reply[
"commands"] = {
"NEW_SESSION",
"GET_KICAD_VERSION",
"LIST_SUPPORTED_VERSIONS",
820 "CAPABILITIES",
"GET_SOURCE_INFO",
"REMOTE_LOGIN",
"DL_SYMBOL",
821 "DL_COMPONENT",
"DL_FOOTPRINT",
"DL_SPICE",
"DL_3DMODEL",
823 reply[
"compression"] = {
"NONE",
"ZSTD" };
824 reply[
"max_message_size"] = 0;
829 if( command == wxS(
"GET_SOURCE_INFO" ) )
831 nlohmann::json reply = nlohmann::json::object();
842 bool authenticated =
false;
847 reply[
"authenticated"] = authenticated;
853 reply[
"supports_direct_downloads"] =
855 reply[
"supports_inline_payloads"] =
861 if( command == wxS(
"REMOTE_LOGIN" ) )
865 sendRpcError( command, messageId, wxS(
"NO_PROVIDER" ),
_(
"No remote provider is currently selected." ) );
870 const bool signOut = params.value(
"sign_out",
false );
871 const bool interactive = params.value(
"interactive",
true );
879 sendRpcError( command, messageId, wxS(
"SIGN_OUT_FAILED" ), error );
885 nlohmann::json reply = nlohmann::json::object();
886 reply[
"authenticated"] =
false;
887 reply[
"signed_out"] =
true;
894 if( !accessToken.IsEmpty() )
896 nlohmann::json reply = nlohmann::json::object();
897 reply[
"authenticated"] =
true;
898 reply[
"provider_id"] = provider.
provider_id.ToStdString();
905 nlohmann::json reply = nlohmann::json::object();
906 reply[
"authenticated"] =
false;
907 reply[
"auth_type"] =
"none";
914 nlohmann::json reply = nlohmann::json::object();
915 reply[
"authenticated"] =
false;
916 reply[
"started"] =
false;
925 sendRpcError( command, messageId, wxS(
"LOGIN_FAILED" ), error );
929 nlohmann::json reply = nlohmann::json::object();
930 reply[
"authenticated"] =
false;
931 reply[
"started"] =
true;
936 if( command == wxS(
"PLACE_COMPONENT" ) || command == wxS(
"DL_COMPONENT" ) || command == wxS(
"DL_SYMBOL" )
937 || command == wxS(
"DL_FOOTPRINT" ) || command == wxS(
"DL_3DMODEL" ) || command == wxS(
"DL_SPICE" ) )
939 const bool placeSymbol = command == wxS(
"PLACE_COMPONENT" )
941 const bool isComponent = command == wxS(
"DL_COMPONENT" ) || command == wxS(
"PLACE_COMPONENT" );
945 if( isComponent && params.contains(
"assets" ) && params[
"assets"].is_array() )
956 sendRpcError( command, messageId, wxS(
"INVALID_PAYLOAD" ), error );
960 std::vector<uint8_t> payload;
961 const std::string compressionStr = compression.IsEmpty() ? std::string()
962 : std::string( compression.ToUTF8().data() );
966 sendRpcError( command, messageId, wxS(
"INVALID_PAYLOAD" ), error );
972 else if( command == wxS(
"DL_SYMBOL" ) )
974 else if( command == wxS(
"DL_FOOTPRINT" ) )
976 else if( command == wxS(
"DL_3DMODEL" ) )
978 else if( command == wxS(
"DL_SPICE" ) )
988 sendRpcError( command, messageId, wxS(
"IMPORT_FAILED" ),
989 error.IsEmpty() ?
_(
"Unable to process provider payload." ) : error );
995 sendRpcError( command, messageId, wxS(
"UNKNOWN_COMMAND" ),
996 wxString::Format(
_(
"Command '%s' is not supported." ), command ) );
1001 wxString& aError,
const std::vector<LIB_ID>& aFootprintLinks )
1004 const bool placeAfterDownload = mode.IsSameAs( wxS(
"PLACE" ),
false );
1006 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false ) && !placeAfterDownload )
1008 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1014 aError =
_(
"Unsupported symbol payload type." );
1020 aError =
_(
"No schematic editor is available to store symbols." );
1028 aError =
_(
"Unable to load schematic settings." );
1037 wxFileName symDir = baseDir;
1038 symDir.AppendDir( wxS(
"symbols" ) );
1039 symDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1043 if( libItemName.IsEmpty() )
1044 libItemName = wxS(
"symbol" );
1048 if( libraryName.IsEmpty() )
1049 libraryName = wxS(
"symbols" );
1055 wxFileName outFile( symDir );
1056 outFile.SetFullName( nickname + wxS(
".kicad_sym" ) );
1062 aError =
_(
"Unable to access the symbol library manager." );
1068 if( !downloadedSymbol )
1071 downloadedSymbol->SetName( libItemName );
1075 downloadedSymbol->SetLibId( savedId );
1080 std::move( downloadedSymbol ), aError ) )
1085 if( placeAfterDownload )
1093 bool aPlaceSymbol, wxString& aError )
1095 nlohmann::json components;
1099 components = nlohmann::json::parse( aPayload.begin(), aPayload.end() );
1101 catch(
const std::exception& e )
1103 aError = wxString::Format(
_(
"Failed to parse component list: %s" ), e.what() );
1107 if( !components.is_array() || components.empty() )
1109 aError =
_(
"Component list must be a non-empty array." );
1115 struct PreparedEntry
1118 nlohmann::json params;
1119 std::vector<uint8_t> content;
1122 std::vector<PreparedEntry> footprints;
1123 std::vector<PreparedEntry> others;
1124 std::vector<PreparedEntry> symbols;
1126 for(
const nlohmann::json& entry : components )
1128 if( !entry.is_object() )
1130 aError =
_(
"Component entries must be objects." );
1134 std::string entryType = entry.value(
"type",
"" );
1136 if( entryType.empty() )
1138 aError =
_(
"Component entry was missing a type." );
1142 std::transform( entryType.begin(), entryType.end(), entryType.begin(),
1143 [](
unsigned char c )
1145 return static_cast<char>( std::tolower( c ) );
1148 std::vector<uint8_t> content;
1153 const std::string compression = entry.value(
"compression", std::string() );
1155 if( !compression.empty() && compression !=
"NONE" )
1157 std::vector<uint8_t>
decoded = content;
1163 wxString entryName = wxString::FromUTF8( entry.value(
"name",
"" ) );
1164 nlohmann::json entryParams = nlohmann::json::object();
1166 if( !libraryName.IsEmpty() )
1167 entryParams[
"library"] = libraryName.ToStdString();
1169 if( !entryName.IsEmpty() )
1170 entryParams[
"name"] = entryName.ToStdString();
1172 if( entryType ==
"symbol" )
1174 entryParams[
"content_type"] =
"KICAD_SYMBOL_V1";
1175 entryParams[
"mode"] = aPlaceSymbol ?
"PLACE" :
"SAVE";
1176 symbols.push_back( { std::move( entryType ), std::move( entryParams ), std::move( content ) } );
1178 else if( entryType ==
"footprint" )
1180 entryParams[
"content_type"] =
"KICAD_FOOTPRINT_V1";
1181 entryParams[
"mode"] =
"SAVE";
1182 footprints.push_back( { std::move( entryType ), std::move( entryParams ), std::move( content ) } );
1184 else if( entryType ==
"3dmodel" )
1186 entryParams[
"content_type"] =
"KICAD_3D_MODEL_STEP";
1187 entryParams[
"mode"] =
"SAVE";
1188 others.push_back( { std::move( entryType ), std::move( entryParams ), std::move( content ) } );
1190 else if( entryType ==
"spice" )
1192 entryParams[
"content_type"] =
"KICAD_SPICE_MODEL_V1";
1193 entryParams[
"mode"] =
"SAVE";
1194 others.push_back( { std::move( entryType ), std::move( entryParams ), std::move( content ) } );
1198 aError = wxString::Format(
_(
"Unsupported component type '%s'." ),
1199 wxString::FromUTF8( entryType.c_str() ) );
1206 std::vector<LIB_ID> footprintLinks;
1208 for(
const PreparedEntry& entry : footprints )
1212 if( !
receiveFootprint( entry.params, entry.content, aError, &resolvedId ) )
1215 footprintLinks.push_back( resolvedId );
1218 for(
const PreparedEntry& entry : others )
1222 if( entry.type ==
"3dmodel" )
1231 for(
const PreparedEntry& entry : symbols )
1233 if( !
receiveSymbol( entry.params, entry.content, aError, footprintLinks ) )
1242 wxString& aError,
LIB_ID* aOutLibId )
1246 aError =
_(
"Unsupported footprint payload type." );
1255 wxFileName fpRoot = baseDir;
1256 fpRoot.AppendDir( wxS(
"footprints" ) );
1257 fpRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1262 if( libraryName.IsEmpty() )
1263 libraryName = wxS(
"footprints" );
1272 wxFileName libDir = fpRoot;
1273 libDir.AppendDir( libNickname + wxS(
".pretty" ) );
1274 libDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1276 wxString fileName = fpItemName + wxS(
".kicad_mod" );
1278 wxFileName outFile( libDir );
1279 outFile.SetFullName( fileName );
1288 aError =
_(
"Unable to load schematic settings." );
1306 *aOutLibId = resolvedId;
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 );
1345 wxFileName spiceDir = baseDir;
1347 spiceDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1351 if( fileName.IsEmpty() )
1356 if( !fileName.Lower().EndsWith( wxS(
".cir" ) ) )
1357 fileName += wxS(
".cir" );
1359 wxFileName outFile( spiceDir );
1360 outFile.SetFullName( fileName );
1370 aError =
_(
"Remote provider metadata is not loaded." );
1380 for(
const nlohmann::json& assetJson : aParams.at(
"assets" ) )
1390 asset.
required = assetJson.value(
"required",
false );
1392 auto sizeIt = assetJson.find(
"size_bytes" );
1394 if( sizeIt != assetJson.end() && sizeIt->is_number_integer() )
1400 aError =
_(
"Manifest assets require asset_type, content_type, size_bytes, and download_url." );
1404 manifest.
assets.push_back( asset );
1436 showMessage(
_(
"Remote provider sign-in was cancelled or failed." ) );
1444 aEvent.GetString(), tokens, error ) )
1463 showMessage(
_(
"Failed to store remote provider tokens securely." ) );
1471 provider->last_account_label = wxS(
"default" );
1472 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
void ReloadLibraryEntry(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH)
Synchronously recreates the plugin for and reloads the given library.
std::optional< LIB_STATUS > LoadLibraryEntry(LIBRARY_TABLE_TYPE aType, const wxString &aNickname)
Synchronously loads the named library to LOADED state for the given type.
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.
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
const wxString GetUniStringLibNickname() const
static wxString GenerateCodeVerifier()
static wxString GenerateState()
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)
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)
bool receiveFootprint(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, wxString &aError, LIB_ID *aOutLibId=nullptr)
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
bool receiveSymbol(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, wxString &aError, const std::vector< LIB_ID > &aFootprintLinks={})
void beginSessionHandshake()
void onOAuthLoopback(wxCommandEvent &aEvent)
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...
#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...
void ApplyFootprintLinks(LIB_SYMBOL &aSymbol, const std::vector< LIB_ID > &aLinks)
Apply a list of footprint LIB_IDs to a symbol about to be saved.
std::unique_ptr< LIB_SYMBOL > LoadRemoteSymbolFromPayload(const std::vector< uint8_t > &aPayload, const wxString &aLibItemName, wxString &aError)
Deserialize a remote-downloaded symbol payload into a LIB_SYMBOL.
LIB_ID BuildRemoteLibId(const wxString &aResolvedLibrary, const wxString &aResolvedItemName)
Build the local LIB_ID for a remote-downloaded library item.
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 SaveRemoteSymbolToLibrary(SYMBOL_LIBRARY_ADAPTER &aAdapter, const wxFileName &aLibraryFile, const wxString &aNickname, bool aGlobalTable, std::unique_ptr< LIB_SYMBOL > aSymbol, wxString &aError)
Save a downloaded symbol into the remote symbol library aNickname backed by aLibraryFile.
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
static std::string decoded(const std::string &aBase64)