From 8a7aa295c89a87c6e3d6b31b8683ed4f52a783a1 Mon Sep 17 00:00:00 2001 From: enteresanlikk <23117378+enteresanlikk@users.noreply.github.com> Date: Sat, 8 Aug 2026 01:57:33 +0300 Subject: [PATCH 1/6] feat(nodeengine): let a node state its own icon and connection points --- .../mcp/application/getallservers/mapper.go | 1 + .../mcp/application/getallservers/response.go | 2 ++ .../nodeengine/credentials/airtable_api.go | 3 +-- .../nodeengine/credentials/anthropic_api.go | 3 +-- .../nodeengine/credentials/chatgpt_api.go | 3 +-- .../nodeengine/credentials/coingecko_api.go | 3 +-- .../nodeengine/credentials/deepl_api.go | 3 +-- .../nodeengine/credentials/deepseek_api.go | 3 +-- .../nodeengine/credentials/discord_api.go | 3 +-- .../nodeengine/credentials/elevenlabs_api.go | 3 +-- .../nodeengine/credentials/facebook_oauth2.go | 3 +-- .../nodeengine/credentials/gemini_api.go | 3 +-- .../nodeengine/credentials/gmail_oauth2.go | 3 +-- .../credentials/google_docs_oauth2.go | 3 +-- .../credentials/google_drive_oauth2.go | 3 +-- .../credentials/google_keep_oauth2.go | 3 +-- .../credentials/google_sheets_oauth2.go | 3 +-- .../credentials/instagram_oauth2.go | 3 +-- .../nodeengine/credentials/linkedin_oauth2.go | 3 +-- .../nodeengine/credentials/notion_oauth2.go | 3 +-- .../nodeengine/credentials/piapi_api.go | 3 +-- .../nodeengine/credentials/sendgrid_api.go | 3 +-- .../nodeengine/credentials/slack_oauth2.go | 3 +-- .../credentials/soundcloud_oauth2.go | 3 +-- .../nodeengine/credentials/sunomusic_api.go | 3 +-- .../nodeengine/credentials/telegram_api.go | 3 +-- .../nodeengine/credentials/tiktok_oauth2.go | 3 +-- .../nodeengine/credentials/veo_api.go | 3 +-- .../nodeengine/credentials/whatsapp_api.go | 3 +-- .../nodeengine/credentials/x_oauth2.go | 3 +-- .../nodeengine/credentials/youtube_oauth2.go | 3 +-- .../domain/credentials/credential_icon.go | 3 +-- .../internal/nodeengine/domain/mcp/server.go | 6 +++++ .../nodeengine/domain/mcp/server_icon.go | 5 +++++ .../internal/nodeengine/domain/nodes/node.go | 22 +++++++++++++++++++ .../nodeengine/domain/nodes/node_handle.go | 6 +++++ .../nodeengine/domain/nodes/node_icon.go | 4 ++-- .../nodes/airtable/createrecord/node.go | 10 +++++++-- .../nodes/airtable/listrecords/node.go | 10 +++++++-- .../nodeengine/nodes/airtable/register.go | 5 ++++- .../nodeengine/nodes/anthropic/chat/node.go | 10 +++++++-- .../nodeengine/nodes/anthropic/register.go | 5 ++++- .../nodeengine/nodes/chatgpt/chat/node.go | 10 +++++++-- .../nodeengine/nodes/chatgpt/register.go | 5 ++++- .../nodes/coingecko/airdroptracker/node.go | 10 +++++++-- .../nodes/coingecko/pricemonitor/node.go | 10 +++++++-- .../nodeengine/nodes/coingecko/register.go | 5 ++++- .../nodeengine/nodes/deepl/register.go | 5 ++++- .../nodeengine/nodes/deepl/translate/node.go | 10 +++++++-- .../nodeengine/nodes/deepseek/chat/node.go | 10 +++++++-- .../nodeengine/nodes/deepseek/register.go | 5 ++++- .../nodeengine/nodes/discord/register.go | 5 ++++- .../nodes/discord/sendmedia/node.go | 10 +++++++-- .../nodes/discord/sendmessage/node.go | 10 +++++++-- .../nodeengine/nodes/elevenlabs/register.go | 5 ++++- .../nodes/elevenlabs/texttospeech/node.go | 10 +++++++-- .../nodes/facebook/publishmediapost/node.go | 10 +++++++-- .../nodes/facebook/publishpost/node.go | 10 +++++++-- .../nodes/facebook/publishstory/node.go | 10 +++++++-- .../nodeengine/nodes/facebook/register.go | 5 ++++- .../nodeengine/nodes/gemini/chat/node.go | 10 +++++++-- .../nodes/gemini/nanobanana/node.go | 10 +++++++-- .../nodeengine/nodes/gemini/register.go | 5 ++++- .../nodes/google/docs/createorupdate/node.go | 10 +++++++-- .../nodes/google/docs/readdocs/node.go | 10 +++++++-- .../nodeengine/nodes/google/docs/register.go | 5 ++++- .../nodes/google/drive/createfolder/node.go | 10 +++++++-- .../nodes/google/drive/createtextfile/node.go | 10 +++++++-- .../nodes/google/drive/getfile/node.go | 10 +++++++-- .../nodeengine/nodes/google/drive/register.go | 5 ++++- .../nodes/google/drive/uploadfile/node.go | 10 +++++++-- .../nodes/google/gmail/organizeemails/node.go | 10 +++++++-- .../nodeengine/nodes/google/gmail/register.go | 5 ++++- .../nodes/google/gmail/searchemails/node.go | 10 +++++++-- .../nodes/google/gmail/sendemail/node.go | 10 +++++++-- .../nodes/google/keep/createnote/node.go | 10 +++++++-- .../nodes/google/keep/deletenote/node.go | 10 +++++++-- .../nodes/google/keep/getnote/node.go | 10 +++++++-- .../nodes/google/keep/listnotes/node.go | 10 +++++++-- .../nodeengine/nodes/google/keep/register.go | 5 ++++- .../sheets/adddatatospreadsheet/node.go | 10 +++++++-- .../google/sheets/createspreadsheet/node.go | 10 +++++++-- .../google/sheets/deletespreadsheet/node.go | 10 +++++++-- .../google/sheets/readspreadsheet/node.go | 10 +++++++-- .../nodes/google/sheets/register.go | 5 ++++- .../nodes/google/sheets/update/node.go | 10 +++++++-- .../nodes/google/youtube/register.go | 5 ++++- .../nodes/google/youtube/uploadvideo/node.go | 10 +++++++-- .../nodes/instagram/publishpost/node.go | 10 +++++++-- .../nodes/instagram/publishreels/node.go | 10 +++++++-- .../nodes/instagram/publishstory/node.go | 10 +++++++-- .../nodeengine/nodes/instagram/register.go | 5 ++++- .../nodes/linkedin/publishmediapost/node.go | 10 +++++++-- .../nodes/linkedin/publishpost/node.go | 10 +++++++-- .../nodeengine/nodes/linkedin/register.go | 5 ++++- .../nodes/notion/createpage/node.go | 10 +++++++-- .../nodeengine/nodes/notion/getpage/node.go | 10 +++++++-- .../nodeengine/nodes/notion/register.go | 5 ++++- .../nodes/notion/searchpages/node.go | 10 +++++++-- .../nodes/notion/updatepage/node.go | 10 +++++++-- .../nodeengine/nodes/piapi/audiogen/node.go | 10 +++++++-- .../nodeengine/nodes/piapi/imagegen/node.go | 10 +++++++-- .../nodeengine/nodes/piapi/register.go | 5 ++++- .../nodeengine/nodes/piapi/videogen/node.go | 10 +++++++-- .../nodeengine/nodes/sendgrid/register.go | 5 ++++- .../nodes/sendgrid/sendemail/node.go | 10 +++++++-- .../nodeengine/nodes/slack/register.go | 5 ++++- .../nodeengine/nodes/slack/sendmedia/node.go | 10 +++++++-- .../nodes/slack/sendmessage/node.go | 10 +++++++-- .../nodes/soundcloud/createplaylist/node.go | 10 +++++++-- .../nodes/soundcloud/createtrack/node.go | 10 +++++++-- .../nodeengine/nodes/soundcloud/register.go | 5 ++++- .../nodes/sunomusic/createlyrics/node.go | 10 +++++++-- .../nodes/sunomusic/createmusic/node.go | 10 +++++++-- .../nodeengine/nodes/sunomusic/register.go | 5 ++++- .../nodes/system/condition/executor.go | 4 ++++ .../nodeengine/nodes/system/condition/node.go | 10 +++++++-- .../nodeengine/nodes/system/sleep/node.go | 9 ++++++-- .../nodeengine/nodes/system/starter/node.go | 7 ++++-- .../nodeengine/nodes/telegram/register.go | 5 ++++- .../nodes/telegram/sendmedia/node.go | 10 +++++++-- .../nodes/telegram/sendmessage/node.go | 10 +++++++-- .../nodes/tiktok/publishpost/node.go | 10 +++++++-- .../nodeengine/nodes/tiktok/register.go | 5 ++++- .../internal/nodeengine/nodes/veo/node.go | 10 +++++++-- .../internal/nodeengine/nodes/veo/register.go | 5 ++++- .../nodeengine/nodes/whatsapp/register.go | 5 ++++- .../nodes/whatsapp/sendmedia/node.go | 10 +++++++-- .../nodes/whatsapp/sendtemplate/node.go | 10 +++++++-- .../nodes/whatsapp/sendtextmessage/node.go | 10 +++++++-- .../nodes/x/publishmediapost/node.go | 10 +++++++-- .../nodeengine/nodes/x/publishpost/node.go | 10 +++++++-- .../internal/nodeengine/nodes/x/register.go | 5 ++++- 133 files changed, 718 insertions(+), 223 deletions(-) create mode 100644 apps/platform-api/internal/nodeengine/domain/mcp/server_icon.go create mode 100644 apps/platform-api/internal/nodeengine/domain/nodes/node_handle.go diff --git a/apps/platform-api/internal/mcp/application/getallservers/mapper.go b/apps/platform-api/internal/mcp/application/getallservers/mapper.go index da6384e..c5b3aef 100644 --- a/apps/platform-api/internal/mcp/application/getallservers/mapper.go +++ b/apps/platform-api/internal/mcp/application/getallservers/mapper.go @@ -17,6 +17,7 @@ func MapGetAllServersQueryToGetAllServersResponse( ID: server.GetID(), Name: server.GetName(), Description: server.GetDescription(), + Icon: server.GetIcon(), Version: server.GetVersion(), Instructions: server.GetInstructions(), URL: url, diff --git a/apps/platform-api/internal/mcp/application/getallservers/response.go b/apps/platform-api/internal/mcp/application/getallservers/response.go index 4365bf5..64e5346 100644 --- a/apps/platform-api/internal/mcp/application/getallservers/response.go +++ b/apps/platform-api/internal/mcp/application/getallservers/response.go @@ -1,6 +1,7 @@ package getallservers import ( + nodeEngineDomainMCP "github.com/blocknextai/platform-api/internal/nodeengine/domain/mcp" nodeEngineDomainNodes "github.com/blocknextai/platform-api/internal/nodeengine/domain/nodes" ) @@ -8,6 +9,7 @@ type ServerResponse struct { ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` + Icon nodeEngineDomainMCP.ServerIcon `json:"icon,omitzero"` Version string `json:"version,omitempty"` Instructions string `json:"instructions,omitempty"` URL string `json:"url,omitempty"` diff --git a/apps/platform-api/internal/nodeengine/credentials/airtable_api.go b/apps/platform-api/internal/nodeengine/credentials/airtable_api.go index 8b48afb..9899eaf 100644 --- a/apps/platform-api/internal/nodeengine/credentials/airtable_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/airtable_api.go @@ -12,8 +12,7 @@ func NewAirtableAPICredential() *domain.Credential { Name: "Airtable", Description: "Airtable personal access token domain.", Icon: domain.CredentialIcon{ - Light: "airtable", - Dark: "airtable", + Brand: "airtable", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/anthropic_api.go b/apps/platform-api/internal/nodeengine/credentials/anthropic_api.go index d0c0025..8f2fd74 100644 --- a/apps/platform-api/internal/nodeengine/credentials/anthropic_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/anthropic_api.go @@ -12,8 +12,7 @@ func NewAnthropicAPICredential() *domain.Credential { Name: "Anthropic", Description: "Anthropic API credentials for accessing Claude models.", Icon: domain.CredentialIcon{ - Light: "anthropic", - Dark: "anthropic", + Brand: "anthropic", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/chatgpt_api.go b/apps/platform-api/internal/nodeengine/credentials/chatgpt_api.go index 2f90773..89a89d0 100644 --- a/apps/platform-api/internal/nodeengine/credentials/chatgpt_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/chatgpt_api.go @@ -12,8 +12,7 @@ func NewChatgptAPICredential() *domain.Credential { Name: "ChatGPT", Description: "OpenAI API credentials for ChatGPT models.", Icon: domain.CredentialIcon{ - Light: "chatgpt", - Dark: "chatgpt", + Brand: "chatgpt", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/coingecko_api.go b/apps/platform-api/internal/nodeengine/credentials/coingecko_api.go index 02a39b3..f68007f 100644 --- a/apps/platform-api/internal/nodeengine/credentials/coingecko_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/coingecko_api.go @@ -12,8 +12,7 @@ func NewCoingeckoAPICredential() *domain.Credential { Name: "CoinGecko", Description: "CoinGecko API credentials for cryptocurrency market data.", Icon: domain.CredentialIcon{ - Light: "coingecko", - Dark: "coingecko", + Brand: "coingecko", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/deepl_api.go b/apps/platform-api/internal/nodeengine/credentials/deepl_api.go index aca7034..8259b15 100644 --- a/apps/platform-api/internal/nodeengine/credentials/deepl_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/deepl_api.go @@ -12,8 +12,7 @@ func NewDeeplAPICredential() *domain.Credential { Name: "DeepL", Description: "DeepL API credentials for translation services.", Icon: domain.CredentialIcon{ - Light: "deepl", - Dark: "deepl", + Brand: "deepl", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/deepseek_api.go b/apps/platform-api/internal/nodeengine/credentials/deepseek_api.go index de15be6..4cb4041 100644 --- a/apps/platform-api/internal/nodeengine/credentials/deepseek_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/deepseek_api.go @@ -12,8 +12,7 @@ func NewDeepseekAPICredential() *domain.Credential { Name: "DeepSeek", Description: "DeepSeek API credentials for AI chat models.", Icon: domain.CredentialIcon{ - Light: "deepseek", - Dark: "deepseek", + Brand: "deepseek", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/discord_api.go b/apps/platform-api/internal/nodeengine/credentials/discord_api.go index d8a70c7..8103f6c 100644 --- a/apps/platform-api/internal/nodeengine/credentials/discord_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/discord_api.go @@ -12,8 +12,7 @@ func NewDiscordAPICredential() *domain.Credential { Name: "Discord", Description: "Discord bot credentials for messaging and media.", Icon: domain.CredentialIcon{ - Light: "discord", - Dark: "discord", + Brand: "discord", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/elevenlabs_api.go b/apps/platform-api/internal/nodeengine/credentials/elevenlabs_api.go index 67d1150..ede5c8e 100644 --- a/apps/platform-api/internal/nodeengine/credentials/elevenlabs_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/elevenlabs_api.go @@ -12,8 +12,7 @@ func NewElevenlabsAPICredential() *domain.Credential { Name: "ElevenLabs", Description: "ElevenLabs API credentials for text-to-speech generation.", Icon: domain.CredentialIcon{ - Light: "elevenlabs", - Dark: "elevenlabs", + Brand: "elevenlabs", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/facebook_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/facebook_oauth2.go index a168631..f5c87d4 100644 --- a/apps/platform-api/internal/nodeengine/credentials/facebook_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/facebook_oauth2.go @@ -19,8 +19,7 @@ func NewFacebookOAuth2Credential(redirectURL string) *domain.Credential { Name: "Facebook", Description: "Facebook OAuth2 credentials for managing pages and posts.", Icon: domain.CredentialIcon{ - Light: "facebook", - Dark: "facebook", + Brand: "facebook", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/gemini_api.go b/apps/platform-api/internal/nodeengine/credentials/gemini_api.go index b8de8cc..7b1e757 100644 --- a/apps/platform-api/internal/nodeengine/credentials/gemini_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/gemini_api.go @@ -12,8 +12,7 @@ func NewGeminiAPICredential() *domain.Credential { Name: "Gemini", Description: "Google Gemini API domain.", Icon: domain.CredentialIcon{ - Light: "gemini", - Dark: "gemini", + Brand: "gemini", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/gmail_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/gmail_oauth2.go index 62768a8..8a1b0e9 100644 --- a/apps/platform-api/internal/nodeengine/credentials/gmail_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/gmail_oauth2.go @@ -19,8 +19,7 @@ func NewGmailOAuth2Credential(redirectURL string) *domain.Credential { Name: "Gmail", Description: "Gmail OAuth2 credentials for sending and reading email.", Icon: domain.CredentialIcon{ - Light: "gmail", - Dark: "gmail", + Brand: "gmail", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/google_docs_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/google_docs_oauth2.go index 4503b2e..1203a52 100644 --- a/apps/platform-api/internal/nodeengine/credentials/google_docs_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/google_docs_oauth2.go @@ -19,8 +19,7 @@ func NewGoogleDocsOAuth2Credential(redirectURL string) *domain.Credential { Name: "Google Docs", Description: "Google Docs OAuth2 credentials for managing documents.", Icon: domain.CredentialIcon{ - Light: "google_docs", - Dark: "google_docs", + Brand: "google_docs", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/google_drive_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/google_drive_oauth2.go index 1c64840..5325b25 100644 --- a/apps/platform-api/internal/nodeengine/credentials/google_drive_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/google_drive_oauth2.go @@ -19,8 +19,7 @@ func NewGoogleDriveOAuth2Credential(redirectURL string) *domain.Credential { Name: "Google Drive", Description: "Google Drive OAuth2 credentials for managing files and folders.", Icon: domain.CredentialIcon{ - Light: "google_drive", - Dark: "google_drive", + Brand: "google_drive", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/google_keep_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/google_keep_oauth2.go index c2a37de..842fb71 100644 --- a/apps/platform-api/internal/nodeengine/credentials/google_keep_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/google_keep_oauth2.go @@ -19,8 +19,7 @@ func NewGoogleKeepOAuth2Credential(redirectURL string) *domain.Credential { Name: "Google Keep", Description: "Google Keep OAuth2 credentials for managing notes.", Icon: domain.CredentialIcon{ - Light: "google_keep", - Dark: "google_keep", + Brand: "google_keep", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/google_sheets_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/google_sheets_oauth2.go index 867be1f..ffba73c 100644 --- a/apps/platform-api/internal/nodeengine/credentials/google_sheets_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/google_sheets_oauth2.go @@ -19,8 +19,7 @@ func NewGoogleSheetsOAuth2Credential(redirectURL string) *domain.Credential { Name: "Google Sheets", Description: "Google Sheets OAuth2 credentials for managing spreadsheets.", Icon: domain.CredentialIcon{ - Light: "google_sheets", - Dark: "google_sheets", + Brand: "google_sheets", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/instagram_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/instagram_oauth2.go index dd3b2df..2dccf3b 100644 --- a/apps/platform-api/internal/nodeengine/credentials/instagram_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/instagram_oauth2.go @@ -19,8 +19,7 @@ func NewInstagramOAuth2Credential(redirectURL string) *domain.Credential { Name: "Instagram", Description: "Instagram OAuth2 credentials for publishing posts and stories.", Icon: domain.CredentialIcon{ - Light: "instagram", - Dark: "instagram", + Brand: "instagram", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/linkedin_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/linkedin_oauth2.go index 6f2e617..22be5a4 100644 --- a/apps/platform-api/internal/nodeengine/credentials/linkedin_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/linkedin_oauth2.go @@ -19,8 +19,7 @@ func NewLinkedinOAuth2Credential(redirectURL string) *domain.Credential { Name: "LinkedIn", Description: "LinkedIn OAuth2 credentials for publishing posts.", Icon: domain.CredentialIcon{ - Light: "linkedin", - Dark: "linkedin", + Brand: "linkedin", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/notion_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/notion_oauth2.go index 789b14c..c07fea6 100644 --- a/apps/platform-api/internal/nodeengine/credentials/notion_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/notion_oauth2.go @@ -18,8 +18,7 @@ func NewNotionOAuth2Credential(redirectURL string) *domain.Credential { Name: "Notion", Description: "Notion OAuth2 credentials for managing pages and databases.", Icon: domain.CredentialIcon{ - Light: "notion", - Dark: "notion", + Brand: "notion", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/piapi_api.go b/apps/platform-api/internal/nodeengine/credentials/piapi_api.go index 55d2b96..1f7541e 100644 --- a/apps/platform-api/internal/nodeengine/credentials/piapi_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/piapi_api.go @@ -12,8 +12,7 @@ func NewPiapiAPICredential() *domain.Credential { Name: "PiApi", Description: "PiAPI credentials for image, video, and audio generation.", Icon: domain.CredentialIcon{ - Light: "piapi", - Dark: "piapi", + Brand: "piapi", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/sendgrid_api.go b/apps/platform-api/internal/nodeengine/credentials/sendgrid_api.go index 0dccde3..783076c 100644 --- a/apps/platform-api/internal/nodeengine/credentials/sendgrid_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/sendgrid_api.go @@ -12,8 +12,7 @@ func NewSendgridAPICredential() *domain.Credential { Name: "SendGrid", Description: "SendGrid API credentials for sending email.", Icon: domain.CredentialIcon{ - Light: "sendgrid", - Dark: "sendgrid", + Brand: "sendgrid", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/slack_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/slack_oauth2.go index 85a612b..55e0c15 100644 --- a/apps/platform-api/internal/nodeengine/credentials/slack_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/slack_oauth2.go @@ -20,8 +20,7 @@ func NewSlackOAuth2Credential(redirectURL string) *domain.Credential { Name: "Slack", Description: "Slack OAuth2 credentials for sending messages and media.", Icon: domain.CredentialIcon{ - Light: "slack", - Dark: "slack", + Brand: "slack", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/soundcloud_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/soundcloud_oauth2.go index 18f6f41..c2a95d6 100644 --- a/apps/platform-api/internal/nodeengine/credentials/soundcloud_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/soundcloud_oauth2.go @@ -18,8 +18,7 @@ func NewSoundcloudOAuth2Credential(redirectURL string) *domain.Credential { Name: "SoundCloud", Description: "SoundCloud OAuth2 credentials for managing tracks and playlists.", Icon: domain.CredentialIcon{ - Light: "soundcloud", - Dark: "soundcloud", + Brand: "soundcloud", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/sunomusic_api.go b/apps/platform-api/internal/nodeengine/credentials/sunomusic_api.go index c4fae3b..1e4e752 100644 --- a/apps/platform-api/internal/nodeengine/credentials/sunomusic_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/sunomusic_api.go @@ -12,8 +12,7 @@ func NewSunomusicAPICredential() *domain.Credential { Name: "Suno Music", Description: "Suno Music API credentials for music and lyrics generation.", Icon: domain.CredentialIcon{ - Light: "sunomusic", - Dark: "sunomusic", + Brand: "sunomusic", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/telegram_api.go b/apps/platform-api/internal/nodeengine/credentials/telegram_api.go index c055b86..f07d78b 100644 --- a/apps/platform-api/internal/nodeengine/credentials/telegram_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/telegram_api.go @@ -12,8 +12,7 @@ func NewTelegramAPICredential() *domain.Credential { Name: "Telegram", Description: "Telegram bot credentials for messaging.", Icon: domain.CredentialIcon{ - Light: "telegram", - Dark: "telegram", + Brand: "telegram", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/tiktok_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/tiktok_oauth2.go index 0c0381e..134ac6a 100644 --- a/apps/platform-api/internal/nodeengine/credentials/tiktok_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/tiktok_oauth2.go @@ -19,8 +19,7 @@ func NewTiktokOAuth2Credential(redirectURL string) *domain.Credential { Name: "TikTok", Description: "TikTok OAuth2 credentials for publishing videos.", Icon: domain.CredentialIcon{ - Light: "tiktok", - Dark: "tiktok", + Brand: "tiktok", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/veo_api.go b/apps/platform-api/internal/nodeengine/credentials/veo_api.go index 5feee76..1b742ed 100644 --- a/apps/platform-api/internal/nodeengine/credentials/veo_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/veo_api.go @@ -12,8 +12,7 @@ func NewVeoAPICredential() *domain.Credential { Name: "Veo", Description: "Google Veo API credentials for video generation.", Icon: domain.CredentialIcon{ - Light: "veo", - Dark: "veo", + Brand: "veo", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/whatsapp_api.go b/apps/platform-api/internal/nodeengine/credentials/whatsapp_api.go index 348b882..0245a5a 100644 --- a/apps/platform-api/internal/nodeengine/credentials/whatsapp_api.go +++ b/apps/platform-api/internal/nodeengine/credentials/whatsapp_api.go @@ -12,8 +12,7 @@ func NewWhatsappAPICredential() *domain.Credential { Name: "WhatsApp", Description: "WhatsApp Business API credentials for sending messages.", Icon: domain.CredentialIcon{ - Light: "whatsapp", - Dark: "whatsapp", + Brand: "whatsapp", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/x_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/x_oauth2.go index 4334caa..e5390d0 100644 --- a/apps/platform-api/internal/nodeengine/credentials/x_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/x_oauth2.go @@ -19,8 +19,7 @@ func NewXOAuth2Credential(redirectURL string) *domain.Credential { Name: "X (Twitter)", Description: "X (Twitter) OAuth2 credentials for publishing posts.", Icon: domain.CredentialIcon{ - Light: "x", - Dark: "x", + Brand: "x", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/credentials/youtube_oauth2.go b/apps/platform-api/internal/nodeengine/credentials/youtube_oauth2.go index fa94184..77c7fb6 100644 --- a/apps/platform-api/internal/nodeengine/credentials/youtube_oauth2.go +++ b/apps/platform-api/internal/nodeengine/credentials/youtube_oauth2.go @@ -19,8 +19,7 @@ func NewYoutubeOAuth2Credential(redirectURL string) *domain.Credential { Name: "YouTube", Description: "YouTube OAuth2 credentials for uploading and managing videos.", Icon: domain.CredentialIcon{ - Light: "youtube", - Dark: "youtube", + Brand: "youtube", }, Schema: &gjs.Schema{ Type: "object", diff --git a/apps/platform-api/internal/nodeengine/domain/credentials/credential_icon.go b/apps/platform-api/internal/nodeengine/domain/credentials/credential_icon.go index f7b107b..526431d 100644 --- a/apps/platform-api/internal/nodeengine/domain/credentials/credential_icon.go +++ b/apps/platform-api/internal/nodeengine/domain/credentials/credential_icon.go @@ -1,6 +1,5 @@ package credentials type CredentialIcon struct { - Light string `json:"light,omitempty"` - Dark string `json:"dark,omitempty"` + Brand string `json:"brand,omitempty"` } diff --git a/apps/platform-api/internal/nodeengine/domain/mcp/server.go b/apps/platform-api/internal/nodeengine/domain/mcp/server.go index cfe38be..66d894a 100644 --- a/apps/platform-api/internal/nodeengine/domain/mcp/server.go +++ b/apps/platform-api/internal/nodeengine/domain/mcp/server.go @@ -8,6 +8,7 @@ type Server struct { ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` + Icon ServerIcon `json:"icon,omitzero"` Version string `json:"version,omitempty"` Instructions string `json:"instructions,omitempty"` Tools []nodes.NodeManager `json:"tools,omitempty"` @@ -18,6 +19,7 @@ type ServerManager interface { GetID() string GetName() string GetDescription() string + GetIcon() ServerIcon GetVersion() string GetInstructions() string GetTools() []nodes.NodeManager @@ -37,6 +39,10 @@ func (s *Server) GetDescription() string { return s.Description } +func (s *Server) GetIcon() ServerIcon { + return s.Icon +} + func (s *Server) GetVersion() string { return s.Version } diff --git a/apps/platform-api/internal/nodeengine/domain/mcp/server_icon.go b/apps/platform-api/internal/nodeengine/domain/mcp/server_icon.go new file mode 100644 index 0000000..af01bfa --- /dev/null +++ b/apps/platform-api/internal/nodeengine/domain/mcp/server_icon.go @@ -0,0 +1,5 @@ +package mcp + +type ServerIcon struct { + Brand string `json:"brand,omitempty"` +} diff --git a/apps/platform-api/internal/nodeengine/domain/nodes/node.go b/apps/platform-api/internal/nodeengine/domain/nodes/node.go index 0d3ea55..3c53509 100644 --- a/apps/platform-api/internal/nodeengine/domain/nodes/node.go +++ b/apps/platform-api/internal/nodeengine/domain/nodes/node.go @@ -10,6 +10,8 @@ type Node struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` Icon NodeIcon `json:"icon"` + Inputs []NodeHandle `json:"inputs"` + Outputs []NodeHandle `json:"outputs"` InputSchema *gjs.Schema `json:"inputSchema,omitempty"` OutputSchema *gjs.Schema `json:"outputSchema,omitempty"` Categories []string `json:"categories,omitempty"` @@ -27,6 +29,10 @@ type NodeManager interface { GetName() string GetDescription() string GetIcon() NodeIcon + GetInputs() []NodeHandle + SetInputs(handles []NodeHandle) + GetOutputs() []NodeHandle + SetOutputs(handles []NodeHandle) GetInputSchema() *gjs.Schema GetOutputSchema() *gjs.Schema GetCategories() []string @@ -58,6 +64,22 @@ func (n *Node) GetIcon() NodeIcon { return n.Icon } +func (n *Node) GetInputs() []NodeHandle { + return n.Inputs +} + +func (n *Node) SetInputs(handles []NodeHandle) { + n.Inputs = handles +} + +func (n *Node) GetOutputs() []NodeHandle { + return n.Outputs +} + +func (n *Node) SetOutputs(handles []NodeHandle) { + n.Outputs = handles +} + func (n *Node) GetInputSchema() *gjs.Schema { return n.InputSchema } diff --git a/apps/platform-api/internal/nodeengine/domain/nodes/node_handle.go b/apps/platform-api/internal/nodeengine/domain/nodes/node_handle.go new file mode 100644 index 0000000..4ca5f10 --- /dev/null +++ b/apps/platform-api/internal/nodeengine/domain/nodes/node_handle.go @@ -0,0 +1,6 @@ +package nodes + +type NodeHandle struct { + Key string `json:"key"` + Label string `json:"label,omitempty"` +} diff --git a/apps/platform-api/internal/nodeengine/domain/nodes/node_icon.go b/apps/platform-api/internal/nodeengine/domain/nodes/node_icon.go index 55d3064..590de8b 100644 --- a/apps/platform-api/internal/nodeengine/domain/nodes/node_icon.go +++ b/apps/platform-api/internal/nodeengine/domain/nodes/node_icon.go @@ -1,6 +1,6 @@ package nodes type NodeIcon struct { - Light string `json:"light,omitempty"` - Dark string `json:"dark,omitempty"` + Brand string `json:"brand,omitempty"` + Glyph string `json:"glyph,omitempty"` } diff --git a/apps/platform-api/internal/nodeengine/nodes/airtable/createrecord/node.go b/apps/platform-api/internal/nodeengine/nodes/airtable/createrecord/node.go index 351c042..bbbf284 100644 --- a/apps/platform-api/internal/nodeengine/nodes/airtable/createrecord/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/airtable/createrecord/node.go @@ -17,8 +17,14 @@ func NewAirtableCreateRecordNode(nodeID string) *AirtableCreateRecordNode { Name: "Airtable Create Record", Description: "Create a new record in an Airtable base.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "airtable", + Glyph: "record", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Database"}, SubCategories: []string{"Airtable"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/airtable/listrecords/node.go b/apps/platform-api/internal/nodeengine/nodes/airtable/listrecords/node.go index 16903dd..c90ea98 100644 --- a/apps/platform-api/internal/nodeengine/nodes/airtable/listrecords/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/airtable/listrecords/node.go @@ -18,8 +18,14 @@ func NewAirtableListRecordsNode(nodeID string) *AirtableListRecordsNode { Name: "Airtable List Records", Description: "List records from an Airtable base.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "airtable", + Glyph: "list", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Database"}, SubCategories: []string{"Airtable"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/airtable/register.go b/apps/platform-api/internal/nodeengine/nodes/airtable/register.go index c78a6ba..bdd6985 100644 --- a/apps/platform-api/internal/nodeengine/nodes/airtable/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/airtable/register.go @@ -35,7 +35,10 @@ func Register() { ID: nodeID, Name: "Airtable", Description: "Tools for managing Airtable records.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "airtable", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ createRecordNode, listRecordsNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/anthropic/chat/node.go b/apps/platform-api/internal/nodeengine/nodes/anthropic/chat/node.go index 306a79b..31e4947 100644 --- a/apps/platform-api/internal/nodeengine/nodes/anthropic/chat/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/anthropic/chat/node.go @@ -18,8 +18,14 @@ func NewAnthropicChatNode(nodeID string) *AnthropicChatNode { Name: "Anthropic Chat", Description: "Generate a chat completion using Anthropic Claude.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "anthropic", + Glyph: "chat", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"AI"}, SubCategories: []string{"Anthropic"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/anthropic/register.go b/apps/platform-api/internal/nodeengine/nodes/anthropic/register.go index fa828b5..967ab6f 100644 --- a/apps/platform-api/internal/nodeengine/nodes/anthropic/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/anthropic/register.go @@ -25,7 +25,10 @@ func Register() { ID: nodeID, Name: "Anthropic", Description: "Tools for chat completions with Anthropic Claude.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "anthropic", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ chatNode, }, diff --git a/apps/platform-api/internal/nodeengine/nodes/chatgpt/chat/node.go b/apps/platform-api/internal/nodeengine/nodes/chatgpt/chat/node.go index e2df7ca..8a9abde 100644 --- a/apps/platform-api/internal/nodeengine/nodes/chatgpt/chat/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/chatgpt/chat/node.go @@ -18,8 +18,14 @@ func NewChatgptChatNode(nodeID string) *ChatgptChatNode { Name: "ChatGPT Chat", Description: "Generate a chat completion using OpenAI ChatGPT.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "chatgpt", + Glyph: "chat", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"AI"}, SubCategories: []string{"ChatGPT"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/chatgpt/register.go b/apps/platform-api/internal/nodeengine/nodes/chatgpt/register.go index 51fd195..cc864a8 100644 --- a/apps/platform-api/internal/nodeengine/nodes/chatgpt/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/chatgpt/register.go @@ -25,7 +25,10 @@ func Register() { ID: nodeID, Name: "ChatGPT", Description: "Tools for chat completions with OpenAI ChatGPT.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "chatgpt", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ chatNode, }, diff --git a/apps/platform-api/internal/nodeengine/nodes/coingecko/airdroptracker/node.go b/apps/platform-api/internal/nodeengine/nodes/coingecko/airdroptracker/node.go index 723f30b..612a04e 100644 --- a/apps/platform-api/internal/nodeengine/nodes/coingecko/airdroptracker/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/coingecko/airdroptracker/node.go @@ -18,8 +18,14 @@ func NewCoingeckoAirdropTrackerNode(nodeID string) *CoingeckoAirdropTrackerNode Name: "Coingecko Airdrop Tracker", Description: "Track cryptocurrency airdrops via CoinGecko.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "coingecko", + Glyph: "target", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Blockchain"}, SubCategories: []string{"CoinGecko"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/coingecko/pricemonitor/node.go b/apps/platform-api/internal/nodeengine/nodes/coingecko/pricemonitor/node.go index 8d73c57..1633abf 100644 --- a/apps/platform-api/internal/nodeengine/nodes/coingecko/pricemonitor/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/coingecko/pricemonitor/node.go @@ -18,8 +18,14 @@ func NewCoingeckoPriceMonitorNode(nodeID string) *CoingeckoPriceMonitorNode { Name: "Coingecko Price Monitor", Description: "Monitor cryptocurrency prices via CoinGecko.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "coingecko", + Glyph: "trending", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Blockchain"}, SubCategories: []string{"CoinGecko"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/coingecko/register.go b/apps/platform-api/internal/nodeengine/nodes/coingecko/register.go index 5d17d7b..00c5752 100644 --- a/apps/platform-api/internal/nodeengine/nodes/coingecko/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/coingecko/register.go @@ -35,7 +35,10 @@ func Register() { ID: nodeID, Name: "CoinGecko", Description: "Tools for tracking cryptocurrency markets via CoinGecko.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "coingecko", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ airdropTrackerNode, priceMonitorNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/deepl/register.go b/apps/platform-api/internal/nodeengine/nodes/deepl/register.go index e6cad32..f00eb4f 100644 --- a/apps/platform-api/internal/nodeengine/nodes/deepl/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/deepl/register.go @@ -25,7 +25,10 @@ func Register() { ID: nodeID, Name: "DeepL", Description: "Tools for translation via DeepL.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "deepl", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ translateNode, }, diff --git a/apps/platform-api/internal/nodeengine/nodes/deepl/translate/node.go b/apps/platform-api/internal/nodeengine/nodes/deepl/translate/node.go index 7b0aa70..3a456b4 100644 --- a/apps/platform-api/internal/nodeengine/nodes/deepl/translate/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/deepl/translate/node.go @@ -18,8 +18,14 @@ func NewDeeplTranslateNode(nodeID string) *DeeplTranslateNode { Name: "DeepL Translate", Description: "Translate text using DeepL.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "deepl", + Glyph: "translate", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"AI"}, SubCategories: []string{"DeepL"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/deepseek/chat/node.go b/apps/platform-api/internal/nodeengine/nodes/deepseek/chat/node.go index 93c87e2..2fa7ebc 100644 --- a/apps/platform-api/internal/nodeengine/nodes/deepseek/chat/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/deepseek/chat/node.go @@ -18,8 +18,14 @@ func NewDeepseekChatNode(nodeID string) *DeepseekChatNode { Name: "DeepSeek Chat", Description: "Generate a chat completion using DeepSeek.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "deepseek", + Glyph: "chat", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"AI"}, SubCategories: []string{"DeepSeek"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/deepseek/register.go b/apps/platform-api/internal/nodeengine/nodes/deepseek/register.go index b1d13d9..b45cc2c 100644 --- a/apps/platform-api/internal/nodeengine/nodes/deepseek/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/deepseek/register.go @@ -25,7 +25,10 @@ func Register() { ID: nodeID, Name: "DeepSeek", Description: "Tools for chat completions with DeepSeek models.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "deepseek", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ chatNode, }, diff --git a/apps/platform-api/internal/nodeengine/nodes/discord/register.go b/apps/platform-api/internal/nodeengine/nodes/discord/register.go index b5d53ee..532fd3a 100644 --- a/apps/platform-api/internal/nodeengine/nodes/discord/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/discord/register.go @@ -40,7 +40,10 @@ func Register(fileGateway filegateway.FileGateway) { ID: nodeID, Name: "Discord", Description: "Tools for sending messages and media to Discord.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "discord", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ sendMessageNode, sendMediaNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/discord/sendmedia/node.go b/apps/platform-api/internal/nodeengine/nodes/discord/sendmedia/node.go index 9fba2f2..556142e 100644 --- a/apps/platform-api/internal/nodeengine/nodes/discord/sendmedia/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/discord/sendmedia/node.go @@ -17,8 +17,14 @@ func NewDiscordSendMediaNode(nodeID string) *DiscordSendMediaNode { Name: "Discord Send Media", Description: "Send a media file to a Discord channel.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "discord", + Glyph: "image", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Discord"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/discord/sendmessage/node.go b/apps/platform-api/internal/nodeengine/nodes/discord/sendmessage/node.go index b1755d3..38ccb15 100644 --- a/apps/platform-api/internal/nodeengine/nodes/discord/sendmessage/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/discord/sendmessage/node.go @@ -17,8 +17,14 @@ func NewDiscordSendMessageNode(nodeID string) *DiscordSendMessageNode { Name: "Discord Send Message", Description: "Send a text message to a Discord channel.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "discord", + Glyph: "chat", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Discord"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/elevenlabs/register.go b/apps/platform-api/internal/nodeengine/nodes/elevenlabs/register.go index 334e379..f602051 100644 --- a/apps/platform-api/internal/nodeengine/nodes/elevenlabs/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/elevenlabs/register.go @@ -26,7 +26,10 @@ func Register(fileGateway filegateway.FileGateway) { ID: nodeID, Name: "ElevenLabs", Description: "Tools for text-to-speech and audio synthesis via ElevenLabs.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "elevenlabs", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ textToSpeechNode, }, diff --git a/apps/platform-api/internal/nodeengine/nodes/elevenlabs/texttospeech/node.go b/apps/platform-api/internal/nodeengine/nodes/elevenlabs/texttospeech/node.go index eb45d91..59b1a9a 100644 --- a/apps/platform-api/internal/nodeengine/nodes/elevenlabs/texttospeech/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/elevenlabs/texttospeech/node.go @@ -18,8 +18,14 @@ func NewElevenlabsTextToSpeechNode(nodeID string) *ElevenlabsTextToSpeechNode { Name: "ElevenLabs Text To Speech", Description: "Convert text to speech using ElevenLabs.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "elevenlabs", + Glyph: "speaker", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Audio"}, SubCategories: []string{"ElevenLabs"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/facebook/publishmediapost/node.go b/apps/platform-api/internal/nodeengine/nodes/facebook/publishmediapost/node.go index 3ca278d..3916eb7 100644 --- a/apps/platform-api/internal/nodeengine/nodes/facebook/publishmediapost/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/facebook/publishmediapost/node.go @@ -17,8 +17,14 @@ func NewFacebookPublishMediaPostNode(nodeID string) *FacebookPublishMediaPostNod Name: "Facebook Publish Media Post", Description: "Publish a post with media to a Facebook page.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "facebook", + Glyph: "image", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Facebook"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/facebook/publishpost/node.go b/apps/platform-api/internal/nodeengine/nodes/facebook/publishpost/node.go index b59f9a6..930a0ba 100644 --- a/apps/platform-api/internal/nodeengine/nodes/facebook/publishpost/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/facebook/publishpost/node.go @@ -17,8 +17,14 @@ func NewFacebookPublishPostNode(nodeID string) *FacebookPublishPostNode { Name: "Facebook Publish Post", Description: "Publish a text post to a Facebook page.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "facebook", + Glyph: "send", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Facebook"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/facebook/publishstory/node.go b/apps/platform-api/internal/nodeengine/nodes/facebook/publishstory/node.go index 5ecda4a..88bd1b8 100644 --- a/apps/platform-api/internal/nodeengine/nodes/facebook/publishstory/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/facebook/publishstory/node.go @@ -17,8 +17,14 @@ func NewFacebookPublishStoryNode(nodeID string) *FacebookPublishStoryNode { Name: "Facebook Publish Story", Description: "Publish a story to a Facebook page.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "facebook", + Glyph: "story", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Facebook"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/facebook/register.go b/apps/platform-api/internal/nodeengine/nodes/facebook/register.go index 5c27891..23261df 100644 --- a/apps/platform-api/internal/nodeengine/nodes/facebook/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/facebook/register.go @@ -45,7 +45,10 @@ func Register() { ID: nodeID, Name: "Facebook", Description: "Tools for publishing posts, media, and stories to Facebook pages.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "facebook", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ publishPostNode, publishMediaPostNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/gemini/chat/node.go b/apps/platform-api/internal/nodeengine/nodes/gemini/chat/node.go index 9f2bf11..b55da49 100644 --- a/apps/platform-api/internal/nodeengine/nodes/gemini/chat/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/gemini/chat/node.go @@ -18,8 +18,14 @@ func NewGeminiChatNode(nodeID string) *GeminiChatNode { Name: "Gemini Chat", Description: "Generate a chat completion using Google Gemini.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "gemini", + Glyph: "chat", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"AI"}, SubCategories: []string{"Gemini"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/gemini/nanobanana/node.go b/apps/platform-api/internal/nodeengine/nodes/gemini/nanobanana/node.go index 0afa5d3..f127e25 100644 --- a/apps/platform-api/internal/nodeengine/nodes/gemini/nanobanana/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/gemini/nanobanana/node.go @@ -18,8 +18,14 @@ func NewGeminiImageGenerationNode(nodeID string) *GeminiImageGenerationNode { Name: "Gemini Nano Banana", Description: "Generate an image from a text prompt using Google Gemini Nano Banana.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "gemini", + Glyph: "image", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Image"}, SubCategories: []string{"Gemini"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/gemini/register.go b/apps/platform-api/internal/nodeengine/nodes/gemini/register.go index c407d3d..a1733e4 100644 --- a/apps/platform-api/internal/nodeengine/nodes/gemini/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/gemini/register.go @@ -36,7 +36,10 @@ func Register(fileGatewayService filegateway.FileGateway) { ID: nodeID, Name: "Gemini", Description: "Tools for chat completions and image generation with Google Gemini.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "gemini", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ nanoBananaNode, chatNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/docs/createorupdate/node.go b/apps/platform-api/internal/nodeengine/nodes/google/docs/createorupdate/node.go index d11d961..9f3bb61 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/docs/createorupdate/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/docs/createorupdate/node.go @@ -17,8 +17,14 @@ func NewGoogleDocsCreateOrUpdateNode(nodeID string) *GoogleDocsCreateOrUpdateNod Name: "Google Docs Create or Update", Description: "Create a new Google Docs document or update an existing one with the provided content.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_docs", + Glyph: "pencil", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Docs"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/docs/readdocs/node.go b/apps/platform-api/internal/nodeengine/nodes/google/docs/readdocs/node.go index b09971b..07ba573 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/docs/readdocs/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/docs/readdocs/node.go @@ -17,8 +17,14 @@ func NewGoogleDocsReadDocsNode(nodeID string) *GoogleDocsReadDocsNode { Name: "Google Docs Read", Description: "Read the plain text content of a Google Docs document.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_docs", + Glyph: "eye", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Docs"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/docs/register.go b/apps/platform-api/internal/nodeengine/nodes/google/docs/register.go index ae35156..314f741 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/docs/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/docs/register.go @@ -35,7 +35,10 @@ func Register() { ID: nodeID, Name: "Google Docs", Description: "Tools for creating, updating, and reading Google Docs.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "google_docs", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ createOrUpdateNode, readDocsNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/drive/createfolder/node.go b/apps/platform-api/internal/nodeengine/nodes/google/drive/createfolder/node.go index 6a4b171..aef5fc5 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/drive/createfolder/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/drive/createfolder/node.go @@ -18,8 +18,14 @@ func NewGoogleDriveCreateFolderNode(nodeID string) *GoogleDriveCreateFolderNode Name: "Google Drive Create Folder", Description: "Create a new folder in Google Drive.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_drive", + Glyph: "folder", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Drive"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/drive/createtextfile/node.go b/apps/platform-api/internal/nodeengine/nodes/google/drive/createtextfile/node.go index fbef502..e65bd91 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/drive/createtextfile/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/drive/createtextfile/node.go @@ -18,8 +18,14 @@ func NewGoogleDriveCreateTextFileNode(nodeID string) *GoogleDriveCreateTextFileN Name: "Google Drive Create Text File", Description: "Create a plain text file in Google Drive.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_drive", + Glyph: "plus", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Drive"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/drive/getfile/node.go b/apps/platform-api/internal/nodeengine/nodes/google/drive/getfile/node.go index 2502084..1fe071e 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/drive/getfile/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/drive/getfile/node.go @@ -17,8 +17,14 @@ func NewGoogleDriveGetFileNode(nodeID string) *GoogleDriveGetFileNode { Name: "Google Drive Get File", Description: "Retrieve a file from Google Drive along with its metadata.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_drive", + Glyph: "file", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Drive"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/drive/register.go b/apps/platform-api/internal/nodeengine/nodes/google/drive/register.go index cc04317..28008c2 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/drive/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/drive/register.go @@ -56,7 +56,10 @@ func Register(fileGateway filegateway.FileGateway) { ID: nodeID, Name: "Google Drive", Description: "Tools for managing files and folders in Google Drive.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "google_drive", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ createFolderNode, createTextFileNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/drive/uploadfile/node.go b/apps/platform-api/internal/nodeengine/nodes/google/drive/uploadfile/node.go index 3781382..c871486 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/drive/uploadfile/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/drive/uploadfile/node.go @@ -18,8 +18,14 @@ func NewGoogleDriveUploadFileNode(nodeID string) *GoogleDriveUploadFileNode { Name: "Google Drive Upload File", Description: "Upload one or more files to Google Drive from a list of source URLs.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_drive", + Glyph: "upload", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Drive"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/gmail/organizeemails/node.go b/apps/platform-api/internal/nodeengine/nodes/google/gmail/organizeemails/node.go index 2b4e7ad..ef01efe 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/gmail/organizeemails/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/gmail/organizeemails/node.go @@ -17,8 +17,14 @@ func NewGmailOrganizeEmailsNode(nodeID string) *GmailOrganizeEmailsNode { Name: "Gmail Organize Emails", Description: "Organize Gmail messages by applying labels, stars, categories, read state, or trash actions to messages matching a search query.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "gmail", + Glyph: "organize", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Mailing"}, SubCategories: []string{"Gmail"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/gmail/register.go b/apps/platform-api/internal/nodeengine/nodes/google/gmail/register.go index f138271..b5932e9 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/gmail/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/gmail/register.go @@ -45,7 +45,10 @@ func Register() { ID: nodeID, Name: "Gmail", Description: "Tools for organizing, searching, and sending Gmail emails.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "gmail", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ organizeEmailsNode, searchEmailsNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/gmail/searchemails/node.go b/apps/platform-api/internal/nodeengine/nodes/google/gmail/searchemails/node.go index 36f126f..ee1b196 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/gmail/searchemails/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/gmail/searchemails/node.go @@ -17,8 +17,14 @@ func NewGmailSearchEmailsNode(nodeID string) *GmailSearchEmailsNode { Name: "Gmail Search Emails", Description: "Search Gmail messages using Gmail's standard search syntax and return matching email summaries.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "gmail", + Glyph: "search", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Mailing"}, SubCategories: []string{"Gmail"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/gmail/sendemail/node.go b/apps/platform-api/internal/nodeengine/nodes/google/gmail/sendemail/node.go index 88eae63..2d34787 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/gmail/sendemail/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/gmail/sendemail/node.go @@ -17,8 +17,14 @@ func NewGmailSendEmailNode(nodeID string) *GmailSendEmailNode { Name: "Gmail Send Email", Description: "Send an email through Gmail.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "gmail", + Glyph: "send", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Mailing"}, SubCategories: []string{"Gmail"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/keep/createnote/node.go b/apps/platform-api/internal/nodeengine/nodes/google/keep/createnote/node.go index 4e4a363..160ef6e 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/keep/createnote/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/keep/createnote/node.go @@ -18,8 +18,14 @@ func NewGoogleKeepCreateNoteNode(nodeID string) *GoogleKeepCreateNoteNode { Name: "Google Keep Create Note", Description: "Create a new note in Google Keep.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_keep", + Glyph: "note", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Keep"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/keep/deletenote/node.go b/apps/platform-api/internal/nodeengine/nodes/google/keep/deletenote/node.go index 714a2ff..77e261d 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/keep/deletenote/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/keep/deletenote/node.go @@ -17,8 +17,14 @@ func NewGoogleKeepDeleteNoteNode(nodeID string) *GoogleKeepDeleteNoteNode { Name: "Google Keep Delete Note", Description: "Delete a note from Google Keep. This removes the note immediately and cannot be undone.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_keep", + Glyph: "trash", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Keep"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/keep/getnote/node.go b/apps/platform-api/internal/nodeengine/nodes/google/keep/getnote/node.go index d4eff62..d0f965c 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/keep/getnote/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/keep/getnote/node.go @@ -18,8 +18,14 @@ func NewGoogleKeepGetNoteNode(nodeID string) *GoogleKeepGetNoteNode { Name: "Google Keep Get Note", Description: "Get a single note from Google Keep by its resource name.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_keep", + Glyph: "eye", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Keep"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/keep/listnotes/node.go b/apps/platform-api/internal/nodeengine/nodes/google/keep/listnotes/node.go index c34a5cb..9aa8d3b 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/keep/listnotes/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/keep/listnotes/node.go @@ -18,8 +18,14 @@ func NewGoogleKeepListNotesNode(nodeID string) *GoogleKeepListNotesNode { Name: "Google Keep List Notes", Description: "List notes from Google Keep with optional filtering and pagination.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_keep", + Glyph: "list", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Keep"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/keep/register.go b/apps/platform-api/internal/nodeengine/nodes/google/keep/register.go index 73ae67a..0365049 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/keep/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/keep/register.go @@ -55,7 +55,10 @@ func Register() { ID: nodeID, Name: "Google Keep", Description: "Tools for creating, reading, listing, and deleting Google Keep notes.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "google_keep", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ createNoteNode, getNoteNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/sheets/adddatatospreadsheet/node.go b/apps/platform-api/internal/nodeengine/nodes/google/sheets/adddatatospreadsheet/node.go index 5e61fc9..84d5bf5 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/sheets/adddatatospreadsheet/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/sheets/adddatatospreadsheet/node.go @@ -18,8 +18,14 @@ func NewGoogleSheetsAddDataToSpreadsheetNode(nodeID string) *GoogleSheetsAddData Version: "0.0.1", Description: "Append rows of data to a Google Sheets spreadsheet.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_sheets", + Glyph: "plus", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Sheets"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/sheets/createspreadsheet/node.go b/apps/platform-api/internal/nodeengine/nodes/google/sheets/createspreadsheet/node.go index 6972c7b..d15a165 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/sheets/createspreadsheet/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/sheets/createspreadsheet/node.go @@ -17,8 +17,14 @@ func NewGoogleSheetsCreateSpreadsheetNode(nodeID string) *GoogleSheetsCreateSpre Name: "Google Sheets Create Spreadsheet", Description: "Create a new Google Sheets spreadsheet with the given title.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_sheets", + Glyph: "table", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Sheets"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/sheets/deletespreadsheet/node.go b/apps/platform-api/internal/nodeengine/nodes/google/sheets/deletespreadsheet/node.go index 955435c..e6771b5 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/sheets/deletespreadsheet/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/sheets/deletespreadsheet/node.go @@ -17,8 +17,14 @@ func NewGoogleSheetsDeleteSpreadsheetNode(nodeID string) *GoogleSheetsDeleteSpre Name: "Google Sheets Delete Spreadsheet", Description: "Delete a Google Sheets spreadsheet by its identifier.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_sheets", + Glyph: "trash", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Sheets"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/sheets/readspreadsheet/node.go b/apps/platform-api/internal/nodeengine/nodes/google/sheets/readspreadsheet/node.go index 1f6ccc8..bfa1a7d 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/sheets/readspreadsheet/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/sheets/readspreadsheet/node.go @@ -18,8 +18,14 @@ func NewGoogleSheetsReadSpreadsheetNode(nodeID string) *GoogleSheetsReadSpreadsh Name: "Google Sheets Read Spreadsheet", Description: "Read rows from a Google Sheets spreadsheet, optionally filtering and limiting results.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_sheets", + Glyph: "eye", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Sheets"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/sheets/register.go b/apps/platform-api/internal/nodeengine/nodes/google/sheets/register.go index 59b59b7..029d65e 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/sheets/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/sheets/register.go @@ -65,7 +65,10 @@ func Register() { ID: nodeID, Name: "Google Sheets", Description: "Tools for creating, reading, updating, and deleting Google Sheets data.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "google_sheets", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ createSpreadsheetNode, readSpreadsheetNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/sheets/update/node.go b/apps/platform-api/internal/nodeengine/nodes/google/sheets/update/node.go index 7956dfd..fa0629f 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/sheets/update/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/sheets/update/node.go @@ -17,8 +17,14 @@ func NewGoogleSheetsUpdateNode(nodeID string) *GoogleSheetsUpdateNode { Name: "Google Sheets Update Cell", Description: "Update a single cell in a Google Sheets spreadsheet by row number and column.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "google_sheets", + Glyph: "pencil", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Google Workspace"}, SubCategories: []string{"Google Sheets"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/youtube/register.go b/apps/platform-api/internal/nodeengine/nodes/google/youtube/register.go index f2253ed..8e67eb6 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/youtube/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/youtube/register.go @@ -26,7 +26,10 @@ func Register(fileGateway filegateway.FileGateway) { ID: nodeID, Name: "YouTube", Description: "Tools for uploading videos to YouTube.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "youtube", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ uploadVideoNode, }, diff --git a/apps/platform-api/internal/nodeengine/nodes/google/youtube/uploadvideo/node.go b/apps/platform-api/internal/nodeengine/nodes/google/youtube/uploadvideo/node.go index 2364c6a..efc3565 100644 --- a/apps/platform-api/internal/nodeengine/nodes/google/youtube/uploadvideo/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/google/youtube/uploadvideo/node.go @@ -18,8 +18,14 @@ func NewYouTubeUploadVideoNode(nodeID string) *YouTubeUploadVideoNode { Name: "YouTube Upload Video", Description: "Upload a video file to YouTube with the given metadata and privacy setting.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "youtube", + Glyph: "film", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"YouTube"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/instagram/publishpost/node.go b/apps/platform-api/internal/nodeengine/nodes/instagram/publishpost/node.go index a035d43..c3c0c7b 100644 --- a/apps/platform-api/internal/nodeengine/nodes/instagram/publishpost/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/instagram/publishpost/node.go @@ -17,8 +17,14 @@ func NewInstagramPublishPostNode(nodeID string) *InstagramPublishPostNode { Name: "Instagram Publish Post", Description: "Publish a post to an Instagram account.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "instagram", + Glyph: "send", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Instagram"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/instagram/publishreels/node.go b/apps/platform-api/internal/nodeengine/nodes/instagram/publishreels/node.go index 4fed3e2..0119d3a 100644 --- a/apps/platform-api/internal/nodeengine/nodes/instagram/publishreels/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/instagram/publishreels/node.go @@ -18,8 +18,14 @@ func NewInstagramPublishReelsNode(nodeID string) *InstagramPublishReelsNode { Name: "Instagram Publish Reels", Description: "Publish a Reel to an Instagram account.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "instagram", + Glyph: "film", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Instagram"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/instagram/publishstory/node.go b/apps/platform-api/internal/nodeengine/nodes/instagram/publishstory/node.go index 166b57a..6faa0bf 100644 --- a/apps/platform-api/internal/nodeengine/nodes/instagram/publishstory/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/instagram/publishstory/node.go @@ -17,8 +17,14 @@ func NewInstagramPublishStoryNode(nodeID string) *InstagramPublishStoryNode { Name: "Instagram Publish Story", Description: "Publish a story to an Instagram account.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "instagram", + Glyph: "story", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Instagram"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/instagram/register.go b/apps/platform-api/internal/nodeengine/nodes/instagram/register.go index 681ba10..284cd16 100644 --- a/apps/platform-api/internal/nodeengine/nodes/instagram/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/instagram/register.go @@ -45,7 +45,10 @@ func Register() { ID: nodeID, Name: "Instagram", Description: "Tools for publishing posts, stories, and reels to Instagram.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "instagram", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ publishPostNode, publishStoryNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/linkedin/publishmediapost/node.go b/apps/platform-api/internal/nodeengine/nodes/linkedin/publishmediapost/node.go index db2159b..0a73013 100644 --- a/apps/platform-api/internal/nodeengine/nodes/linkedin/publishmediapost/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/linkedin/publishmediapost/node.go @@ -18,8 +18,14 @@ func NewLinkedinPublishMediaPostNode(nodeID string) *LinkedinPublishMediaPostNod Name: "LinkedIn Publish Media Post", Description: "Publish a post with media to a LinkedIn account.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "linkedin", + Glyph: "image", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"LinkedIn"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/linkedin/publishpost/node.go b/apps/platform-api/internal/nodeengine/nodes/linkedin/publishpost/node.go index 8a50651..e2cabed 100644 --- a/apps/platform-api/internal/nodeengine/nodes/linkedin/publishpost/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/linkedin/publishpost/node.go @@ -18,8 +18,14 @@ func NewLinkedinPublishPostNode(nodeID string) *LinkedinPublishPostNode { Name: "LinkedIn Publish Post", Description: "Publish a post to a LinkedIn account.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "linkedin", + Glyph: "send", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"LinkedIn"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/linkedin/register.go b/apps/platform-api/internal/nodeengine/nodes/linkedin/register.go index df601f3..ff9f26e 100644 --- a/apps/platform-api/internal/nodeengine/nodes/linkedin/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/linkedin/register.go @@ -36,7 +36,10 @@ func Register(fileGateway filegateway.FileGateway) { ID: nodeID, Name: "LinkedIn", Description: "Tools for publishing posts and media to LinkedIn.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "linkedin", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ publishPostNode, publishMediaPostNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/notion/createpage/node.go b/apps/platform-api/internal/nodeengine/nodes/notion/createpage/node.go index 260a421..1954920 100644 --- a/apps/platform-api/internal/nodeengine/nodes/notion/createpage/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/notion/createpage/node.go @@ -17,8 +17,14 @@ func NewNotionCreatePageNode(nodeID string) *NotionCreatePageNode { Name: "Notion Create Page", Description: "Create a new page in a Notion workspace.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "notion", + Glyph: "plus", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Notion"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/notion/getpage/node.go b/apps/platform-api/internal/nodeengine/nodes/notion/getpage/node.go index 54c6417..c51b57d 100644 --- a/apps/platform-api/internal/nodeengine/nodes/notion/getpage/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/notion/getpage/node.go @@ -17,8 +17,14 @@ func NewNotionGetPageNode(nodeID string) *NotionGetPageNode { Name: "Notion Get Page", Description: "Retrieve a page from a Notion workspace.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "notion", + Glyph: "eye", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Notion"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/notion/register.go b/apps/platform-api/internal/nodeengine/nodes/notion/register.go index 42b234b..abdde39 100644 --- a/apps/platform-api/internal/nodeengine/nodes/notion/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/notion/register.go @@ -55,7 +55,10 @@ func Register() { ID: nodeID, Name: "Notion", Description: "Tools for managing Notion pages.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "notion", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ createPageNode, updatePageNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/notion/searchpages/node.go b/apps/platform-api/internal/nodeengine/nodes/notion/searchpages/node.go index 6e678ab..ff88778 100644 --- a/apps/platform-api/internal/nodeengine/nodes/notion/searchpages/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/notion/searchpages/node.go @@ -18,8 +18,14 @@ func NewNotionSearchPagesNode(nodeID string) *NotionSearchPagesNode { Name: "Notion Search Pages", Description: "Search for pages in a Notion workspace.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "notion", + Glyph: "search", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Notion"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/notion/updatepage/node.go b/apps/platform-api/internal/nodeengine/nodes/notion/updatepage/node.go index 29e0b29..5c236d5 100644 --- a/apps/platform-api/internal/nodeengine/nodes/notion/updatepage/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/notion/updatepage/node.go @@ -17,8 +17,14 @@ func NewNotionUpdatePageNode(nodeID string) *NotionUpdatePageNode { Name: "Notion Update Page", Description: "Update an existing page in a Notion workspace.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "notion", + Glyph: "pencil", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Notion"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/piapi/audiogen/node.go b/apps/platform-api/internal/nodeengine/nodes/piapi/audiogen/node.go index 0b45fd2..b4e3dbb 100644 --- a/apps/platform-api/internal/nodeengine/nodes/piapi/audiogen/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/piapi/audiogen/node.go @@ -18,8 +18,14 @@ func NewPiAPIAudioGenNode(nodeID string) *PiAPIAudioGenNode { Name: "PiAPI Audio Generation", Description: "Generate audio using PiAPI.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "piapi", + Glyph: "speaker", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Audio"}, SubCategories: []string{"PiApi"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/piapi/imagegen/node.go b/apps/platform-api/internal/nodeengine/nodes/piapi/imagegen/node.go index d2b317f..6d2365d 100644 --- a/apps/platform-api/internal/nodeengine/nodes/piapi/imagegen/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/piapi/imagegen/node.go @@ -18,8 +18,14 @@ func NewPiAPIImageGenNode(nodeID string) *PiAPIImageGenNode { Name: "PiAPI Image Generation", Description: "Generate an image using PiAPI.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "piapi", + Glyph: "image", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Image"}, SubCategories: []string{"PiApi"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/piapi/register.go b/apps/platform-api/internal/nodeengine/nodes/piapi/register.go index 32654d9..aac2896 100644 --- a/apps/platform-api/internal/nodeengine/nodes/piapi/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/piapi/register.go @@ -45,7 +45,10 @@ func Register() { ID: nodeID, Name: "PiAPI", Description: "Tools for AI image, video, and audio generation via PiAPI.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "piapi", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ imageGenNode, videoGenNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/piapi/videogen/node.go b/apps/platform-api/internal/nodeengine/nodes/piapi/videogen/node.go index b42d930..430d7ad 100644 --- a/apps/platform-api/internal/nodeengine/nodes/piapi/videogen/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/piapi/videogen/node.go @@ -18,8 +18,14 @@ func NewPiAPIVideoGenNode(nodeID string) *PiAPIVideoGenNode { Name: "PiAPI Video Generation", Description: "Generate a video using PiAPI.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "piapi", + Glyph: "film", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Video"}, SubCategories: []string{"PiApi"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/sendgrid/register.go b/apps/platform-api/internal/nodeengine/nodes/sendgrid/register.go index 5732082..92b0f6c 100644 --- a/apps/platform-api/internal/nodeengine/nodes/sendgrid/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/sendgrid/register.go @@ -23,7 +23,10 @@ func Register() { ID: "sendgrid", Name: "SendGrid", Description: "Tools for sending emails via SendGrid.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "sendgrid", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ sendEmailNode, }, diff --git a/apps/platform-api/internal/nodeengine/nodes/sendgrid/sendemail/node.go b/apps/platform-api/internal/nodeengine/nodes/sendgrid/sendemail/node.go index 7aaddfa..f3fab1a 100644 --- a/apps/platform-api/internal/nodeengine/nodes/sendgrid/sendemail/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/sendgrid/sendemail/node.go @@ -17,8 +17,14 @@ func NewSendgridSendEmailNode(nodeID string) *SendgridSendEmailNode { Name: "SendGrid Send Email", Description: "Send an email through SendGrid.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "sendgrid", + Glyph: "send", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Mailing"}, SubCategories: []string{"SendGrid"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/slack/register.go b/apps/platform-api/internal/nodeengine/nodes/slack/register.go index 8beb2c4..793c6fd 100644 --- a/apps/platform-api/internal/nodeengine/nodes/slack/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/slack/register.go @@ -40,7 +40,10 @@ func Register(fileGateway filegateway.FileGateway) { ID: nodeID, Name: "Slack", Description: "Tools for sending messages and media to Slack.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "slack", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ sendMessageNode, sendMediaNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/slack/sendmedia/node.go b/apps/platform-api/internal/nodeengine/nodes/slack/sendmedia/node.go index da3302a..a6a5fe7 100644 --- a/apps/platform-api/internal/nodeengine/nodes/slack/sendmedia/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/slack/sendmedia/node.go @@ -17,8 +17,14 @@ func NewSlackSendMediaNode(nodeID string) *SlackSendMediaNode { Name: "Slack Send Media", Description: "Send a media file to a Slack channel.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "slack", + Glyph: "image", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Slack"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/slack/sendmessage/node.go b/apps/platform-api/internal/nodeengine/nodes/slack/sendmessage/node.go index e7cbcbb..7edc676 100644 --- a/apps/platform-api/internal/nodeengine/nodes/slack/sendmessage/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/slack/sendmessage/node.go @@ -17,8 +17,14 @@ func NewSlackSendMessageNode(nodeID string) *SlackSendMessageNode { Name: "Slack Send Message", Description: "Send a text message to a Slack channel.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "slack", + Glyph: "chat", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Slack"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/soundcloud/createplaylist/node.go b/apps/platform-api/internal/nodeengine/nodes/soundcloud/createplaylist/node.go index 573fa9d..f726789 100644 --- a/apps/platform-api/internal/nodeengine/nodes/soundcloud/createplaylist/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/soundcloud/createplaylist/node.go @@ -17,8 +17,14 @@ func NewSoundCloudCreatePlaylistNode(nodeID string) *SoundCloudCreatePlaylistNod Name: "SoundCloud Create Playlist", Description: "Create a new playlist on SoundCloud.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "soundcloud", + Glyph: "list", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"SoundCloud"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/soundcloud/createtrack/node.go b/apps/platform-api/internal/nodeengine/nodes/soundcloud/createtrack/node.go index c3a2a90..99e90d0 100644 --- a/apps/platform-api/internal/nodeengine/nodes/soundcloud/createtrack/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/soundcloud/createtrack/node.go @@ -17,8 +17,14 @@ func NewSoundCloudCreateTrackNode(nodeID string) *SoundCloudCreateTrackNode { Name: "SoundCloud Create Track", Description: "Upload a new track to SoundCloud.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "soundcloud", + Glyph: "music", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"SoundCloud"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/soundcloud/register.go b/apps/platform-api/internal/nodeengine/nodes/soundcloud/register.go index 9c062f2..c62f1a5 100644 --- a/apps/platform-api/internal/nodeengine/nodes/soundcloud/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/soundcloud/register.go @@ -36,7 +36,10 @@ func Register(fileGateway filegateway.FileGateway) { ID: nodeID, Name: "SoundCloud", Description: "Tools for managing SoundCloud tracks and playlists.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "soundcloud", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ createTrackNode, createPlaylistNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/sunomusic/createlyrics/node.go b/apps/platform-api/internal/nodeengine/nodes/sunomusic/createlyrics/node.go index 2afaef4..e7578cc 100644 --- a/apps/platform-api/internal/nodeengine/nodes/sunomusic/createlyrics/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/sunomusic/createlyrics/node.go @@ -17,8 +17,14 @@ func NewSunoMusicCreateLyricsNode(nodeID string) *SunoMusicCreateLyricsNode { Name: "Suno Create Lyrics", Description: "Generate song lyrics using Suno.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "sunomusic", + Glyph: "note", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Audio"}, SubCategories: []string{"Suno"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/sunomusic/createmusic/node.go b/apps/platform-api/internal/nodeengine/nodes/sunomusic/createmusic/node.go index 37cce16..5754cad 100644 --- a/apps/platform-api/internal/nodeengine/nodes/sunomusic/createmusic/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/sunomusic/createmusic/node.go @@ -18,8 +18,14 @@ func NewSunoMusicCreateMusicNode(nodeID string) *SunoMusicCreateMusicNode { Name: "Suno Create Music", Description: "Generate a music track using Suno.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "sunomusic", + Glyph: "music", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Audio"}, SubCategories: []string{"Suno"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/sunomusic/register.go b/apps/platform-api/internal/nodeengine/nodes/sunomusic/register.go index 3a0a3e3..9baf176 100644 --- a/apps/platform-api/internal/nodeengine/nodes/sunomusic/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/sunomusic/register.go @@ -35,7 +35,10 @@ func Register() { ID: nodeID, Name: "Suno Music", Description: "Tools for generating music and lyrics via Suno.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "sunomusic", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ createMusicNode, createLyricsNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/system/condition/executor.go b/apps/platform-api/internal/nodeengine/nodes/system/condition/executor.go index aff67ae..80accd9 100644 --- a/apps/platform-api/internal/nodeengine/nodes/system/condition/executor.go +++ b/apps/platform-api/internal/nodeengine/nodes/system/condition/executor.go @@ -42,6 +42,10 @@ func (e *ConditionExecutor) ExecuteWithContext(ctx context.Context, credentials case <-ctx.Done(): return nil, ctx.Err() default: + // TODO: only the first item is evaluated, so a node fed 10 items decides + // the whole flow from item 0. Routing items per branch needs the runner + // to carry item identity and keep outputs per edge; see the branching + // notes before changing this. input, err := e.validator.Parse(data[0]) if err != nil { return nil, err diff --git a/apps/platform-api/internal/nodeengine/nodes/system/condition/node.go b/apps/platform-api/internal/nodeengine/nodes/system/condition/node.go index 5a08cd5..5dfd749 100644 --- a/apps/platform-api/internal/nodeengine/nodes/system/condition/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/system/condition/node.go @@ -18,8 +18,14 @@ func NewConditionNode(nodeID string) *ConditionNode { Name: "Condition", Description: "Branch the workflow based on a conditional expression.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Glyph: "branch", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "true", Label: "True"}, + {Key: "false", Label: "False"}, }, Categories: []string{"System"}, SubCategories: []string{"System"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/system/sleep/node.go b/apps/platform-api/internal/nodeengine/nodes/system/sleep/node.go index 4c9d0f2..7f1c0a8 100644 --- a/apps/platform-api/internal/nodeengine/nodes/system/sleep/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/system/sleep/node.go @@ -18,8 +18,13 @@ func NewSleepNode(nodeID string) *SleepNode { Name: "Sleep", Description: "Pause the workflow for a specified duration.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Glyph: "clock", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"System"}, SubCategories: []string{"System"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/system/starter/node.go b/apps/platform-api/internal/nodeengine/nodes/system/starter/node.go index 900cf35..eb97499 100644 --- a/apps/platform-api/internal/nodeengine/nodes/system/starter/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/system/starter/node.go @@ -17,8 +17,11 @@ func NewStarterNode(nodeID string) *StarterNode { Name: "Starter", Description: "Entry point that starts the workflow execution.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Glyph: "play", + }, + Inputs: []nodes.NodeHandle{}, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"System"}, SubCategories: []string{"System"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/telegram/register.go b/apps/platform-api/internal/nodeengine/nodes/telegram/register.go index ed65556..cf7037b 100644 --- a/apps/platform-api/internal/nodeengine/nodes/telegram/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/telegram/register.go @@ -39,7 +39,10 @@ func Register() { ID: nodeID, Name: "Telegram", Description: "Tools for sending messages and media via Telegram.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "telegram", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ sendMessageNode, sendMediaNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/telegram/sendmedia/node.go b/apps/platform-api/internal/nodeengine/nodes/telegram/sendmedia/node.go index bf46e3d..e4dd68c 100644 --- a/apps/platform-api/internal/nodeengine/nodes/telegram/sendmedia/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/telegram/sendmedia/node.go @@ -17,8 +17,14 @@ func NewTelegramSendMediaNode(nodeID string) *TelegramSendMediaNode { Name: "Telegram Send Media", Description: "Send a media file to a Telegram chat.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "telegram", + Glyph: "image", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Telegram"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/telegram/sendmessage/node.go b/apps/platform-api/internal/nodeengine/nodes/telegram/sendmessage/node.go index b37ec4e..79400e5 100644 --- a/apps/platform-api/internal/nodeengine/nodes/telegram/sendmessage/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/telegram/sendmessage/node.go @@ -17,8 +17,14 @@ func NewTelegramSendMessageNode(nodeID string) *TelegramSendMessageNode { Name: "Telegram Send Message", Description: "Send a text message to a Telegram chat.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "telegram", + Glyph: "chat", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"Telegram"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/tiktok/publishpost/node.go b/apps/platform-api/internal/nodeengine/nodes/tiktok/publishpost/node.go index bdbf453..d65dbff 100644 --- a/apps/platform-api/internal/nodeengine/nodes/tiktok/publishpost/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/tiktok/publishpost/node.go @@ -17,8 +17,14 @@ func NewTiktokPublishPostNode(nodeID string) *TiktokPublishPostNode { Name: "TikTok Publish Post", Description: "Publish a video post to a TikTok account.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "tiktok", + Glyph: "send", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"TikTok"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/tiktok/register.go b/apps/platform-api/internal/nodeengine/nodes/tiktok/register.go index d9fcdec..b83f706 100644 --- a/apps/platform-api/internal/nodeengine/nodes/tiktok/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/tiktok/register.go @@ -26,7 +26,10 @@ func Register(fileGateway filegateway.FileGateway) { ID: nodeID, Name: "TikTok", Description: "Tools for publishing posts to TikTok.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "tiktok", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ publishPostNode, }, diff --git a/apps/platform-api/internal/nodeengine/nodes/veo/node.go b/apps/platform-api/internal/nodeengine/nodes/veo/node.go index 923b3fd..9d3dcda 100644 --- a/apps/platform-api/internal/nodeengine/nodes/veo/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/veo/node.go @@ -18,8 +18,14 @@ func NewVeoNode(nodeID string) *VeoNode { Name: "Veo", Description: "Generate a video from a text prompt using Veo.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "veo", + Glyph: "film", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Video"}, SubCategories: []string{"Veo"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/veo/register.go b/apps/platform-api/internal/nodeengine/nodes/veo/register.go index 2f60b1d..7ad3107 100644 --- a/apps/platform-api/internal/nodeengine/nodes/veo/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/veo/register.go @@ -24,7 +24,10 @@ func Register(fileGatewayService filegateway.FileGateway) { ID: nodeID, Name: "Veo", Description: "Tools for video generation via Google Veo.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "veo", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ veoNode, }, diff --git a/apps/platform-api/internal/nodeengine/nodes/whatsapp/register.go b/apps/platform-api/internal/nodeengine/nodes/whatsapp/register.go index 05ff32a..abcc46a 100644 --- a/apps/platform-api/internal/nodeengine/nodes/whatsapp/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/whatsapp/register.go @@ -49,7 +49,10 @@ func Register() { ID: nodeID, Name: "WhatsApp", Description: "Tools for sending text messages, templates, and media via WhatsApp.", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "whatsapp", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ sendMessageNode, sendTemplateNode, diff --git a/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendmedia/node.go b/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendmedia/node.go index 084463d..6b5dbb6 100644 --- a/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendmedia/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendmedia/node.go @@ -17,8 +17,14 @@ func NewWhatsAppSendMediaNode(nodeID string) *WhatsAppSendMediaNode { Name: "WhatsApp Send Media", Description: "Send a media message via WhatsApp.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "whatsapp", + Glyph: "image", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"WhatsApp"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendtemplate/node.go b/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendtemplate/node.go index 493d8e9..55bb508 100644 --- a/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendtemplate/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendtemplate/node.go @@ -18,8 +18,14 @@ func NewWhatsAppSendTemplateNode(nodeID string) *WhatsAppSendTemplateNode { Name: "WhatsApp Send Template", Description: "Send a template message via WhatsApp.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "whatsapp", + Glyph: "file", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"WhatsApp"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendtextmessage/node.go b/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendtextmessage/node.go index 5e958aa..b3a097e 100644 --- a/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendtextmessage/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/whatsapp/sendtextmessage/node.go @@ -17,8 +17,14 @@ func NewWhatsAppSendTextMessageNode(nodeID string) *WhatsAppSendTextMessageNode Name: "WhatsApp Send Text Message", Description: "Send a text message via WhatsApp.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "whatsapp", + Glyph: "chat", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"WhatsApp"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/x/publishmediapost/node.go b/apps/platform-api/internal/nodeengine/nodes/x/publishmediapost/node.go index a68e853..c222446 100644 --- a/apps/platform-api/internal/nodeengine/nodes/x/publishmediapost/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/x/publishmediapost/node.go @@ -17,8 +17,14 @@ func NewXPublishMediaPostNode(nodeID string) *XPublishMediaPostNode { Name: "X Publish Media Post", Description: "Publish a post with media to an X (Twitter) account.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "x", + Glyph: "image", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"X"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/x/publishpost/node.go b/apps/platform-api/internal/nodeengine/nodes/x/publishpost/node.go index 9d6e781..9dc1753 100644 --- a/apps/platform-api/internal/nodeengine/nodes/x/publishpost/node.go +++ b/apps/platform-api/internal/nodeengine/nodes/x/publishpost/node.go @@ -18,8 +18,14 @@ func NewXPublishPostNode(nodeID string) *XPublishPostNode { Name: "X Publish Post", Description: "Publish a post to an X (Twitter) account.", Icon: nodes.NodeIcon{ - Light: nodeID, - Dark: nodeID, + Brand: "x", + Glyph: "send", + }, + Inputs: []nodes.NodeHandle{ + {Key: "in"}, + }, + Outputs: []nodes.NodeHandle{ + {Key: "out"}, }, Categories: []string{"Publishing"}, SubCategories: []string{"X"}, diff --git a/apps/platform-api/internal/nodeengine/nodes/x/register.go b/apps/platform-api/internal/nodeengine/nodes/x/register.go index db55824..b04b03f 100644 --- a/apps/platform-api/internal/nodeengine/nodes/x/register.go +++ b/apps/platform-api/internal/nodeengine/nodes/x/register.go @@ -36,7 +36,10 @@ func Register(fileGateway filegateway.FileGateway) { ID: nodeID, Name: "X", Description: "Tools for publishing posts and media to X (Twitter).", - Version: "0.0.1", + Icon: mcp.ServerIcon{ + Brand: "x", + }, + Version: "0.0.1", Tools: []nodes.NodeManager{ publishPostNode, publishMediaPostNode, From 999d5dd28f507e1861ace31fdc9cb8593c73b7d7 Mon Sep 17 00:00:00 2001 From: enteresanlikk <23117378+enteresanlikk@users.noreply.github.com> Date: Sat, 8 Aug 2026 01:57:53 +0300 Subject: [PATCH 2/6] feat(platform): ship brand marks and action glyphs as files --- .../assets/icons/brands/airtable/dark.svg | 3 + .../assets/icons/brands/airtable/light.svg | 3 + .../assets/icons/brands/anthropic/dark.svg | 6 ++ .../assets/icons/brands/anthropic/light.svg | 6 ++ .../assets/icons/brands/chatgpt/dark.svg | 6 ++ .../assets/icons/brands/chatgpt/light.svg | 6 ++ .../assets/icons/brands/coingecko/dark.svg | 45 ++++++++++++ .../assets/icons/brands/coingecko/light.svg | 45 ++++++++++++ .../public/assets/icons/brands/deepl/dark.svg | 17 +++++ .../assets/icons/brands/deepl/light.svg | 17 +++++ .../assets/icons/brands/deepseek/dark.svg | 6 ++ .../assets/icons/brands/deepseek/light.svg | 6 ++ .../assets/icons/brands/discord/dark.svg | 24 +++++++ .../assets/icons/brands/discord/light.svg | 24 +++++++ .../assets/icons/brands/elevenlabs/dark.svg | 4 ++ .../assets/icons/brands/elevenlabs/light.svg | 4 ++ .../assets/icons/brands/facebook/dark.svg | 38 ++++++++++ .../assets/icons/brands/facebook/light.svg | 38 ++++++++++ .../assets/icons/brands/gemini/dark.svg | 20 ++++++ .../assets/icons/brands/gemini/light.svg | 20 ++++++ .../public/assets/icons/brands/gmail/dark.svg | 22 ++++++ .../assets/icons/brands/gmail/light.svg | 22 ++++++ .../assets/icons/brands/google_docs/dark.svg | 11 +++ .../assets/icons/brands/google_docs/light.svg | 11 +++ .../assets/icons/brands/google_drive/dark.svg | 26 +++++++ .../icons/brands/google_drive/light.svg | 26 +++++++ .../assets/icons/brands/google_keep/dark.svg | 3 + .../assets/icons/brands/google_keep/light.svg | 3 + .../icons/brands/google_sheets/dark.svg | 14 ++++ .../icons/brands/google_sheets/light.svg | 14 ++++ .../assets/icons/brands/instagram/dark.svg | 71 +++++++++++++++++++ .../assets/icons/brands/instagram/light.svg | 71 +++++++++++++++++++ .../assets/icons/brands/linkedin/dark.svg | 14 ++++ .../assets/icons/brands/linkedin/light.svg | 14 ++++ .../assets/icons/brands/notion/dark.svg | 20 ++++++ .../assets/icons/brands/notion/light.svg | 20 ++++++ .../public/assets/icons/brands/piapi/dark.svg | 6 ++ .../assets/icons/brands/piapi/light.svg | 6 ++ .../assets/icons/brands/sendgrid/dark.svg | 9 +++ .../assets/icons/brands/sendgrid/light.svg | 9 +++ .../public/assets/icons/brands/slack/dark.svg | 22 ++++++ .../assets/icons/brands/slack/light.svg | 22 ++++++ .../assets/icons/brands/soundcloud/dark.svg | 6 ++ .../assets/icons/brands/soundcloud/light.svg | 6 ++ .../assets/icons/brands/sunomusic/dark.svg | 8 +++ .../assets/icons/brands/sunomusic/light.svg | 8 +++ .../assets/icons/brands/telegram/dark.svg | 25 +++++++ .../assets/icons/brands/telegram/light.svg | 25 +++++++ .../assets/icons/brands/tiktok/dark.svg | 14 ++++ .../assets/icons/brands/tiktok/light.svg | 14 ++++ .../public/assets/icons/brands/veo/dark.svg | 20 ++++++ .../public/assets/icons/brands/veo/light.svg | 20 ++++++ .../assets/icons/brands/whatsapp/dark.svg | 24 +++++++ .../assets/icons/brands/whatsapp/light.svg | 24 +++++++ .../public/assets/icons/brands/x/dark.svg | 6 ++ .../public/assets/icons/brands/x/light.svg | 6 ++ .../assets/icons/brands/youtube/dark.svg | 22 ++++++ .../assets/icons/brands/youtube/light.svg | 22 ++++++ .../public/assets/icons/glyphs/branch.svg | 3 + .../public/assets/icons/glyphs/chat.svg | 3 + .../public/assets/icons/glyphs/clock.svg | 3 + .../public/assets/icons/glyphs/eye.svg | 3 + .../public/assets/icons/glyphs/file.svg | 3 + .../public/assets/icons/glyphs/film.svg | 3 + .../public/assets/icons/glyphs/folder.svg | 3 + .../public/assets/icons/glyphs/image.svg | 3 + .../public/assets/icons/glyphs/list.svg | 3 + .../public/assets/icons/glyphs/music.svg | 3 + .../public/assets/icons/glyphs/note.svg | 3 + .../public/assets/icons/glyphs/organize.svg | 3 + .../public/assets/icons/glyphs/pencil.svg | 3 + .../public/assets/icons/glyphs/play.svg | 3 + .../public/assets/icons/glyphs/plus.svg | 3 + .../public/assets/icons/glyphs/record.svg | 3 + .../public/assets/icons/glyphs/search.svg | 3 + .../public/assets/icons/glyphs/send.svg | 3 + .../public/assets/icons/glyphs/speaker.svg | 3 + .../public/assets/icons/glyphs/story.svg | 3 + .../public/assets/icons/glyphs/table.svg | 3 + .../public/assets/icons/glyphs/target.svg | 3 + .../public/assets/icons/glyphs/translate.svg | 3 + .../public/assets/icons/glyphs/trash.svg | 3 + .../public/assets/icons/glyphs/trending.svg | 3 + .../public/assets/icons/glyphs/upload.svg | 3 + 84 files changed, 1102 insertions(+) create mode 100644 apps/platform/public/assets/icons/brands/airtable/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/airtable/light.svg create mode 100644 apps/platform/public/assets/icons/brands/anthropic/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/anthropic/light.svg create mode 100644 apps/platform/public/assets/icons/brands/chatgpt/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/chatgpt/light.svg create mode 100644 apps/platform/public/assets/icons/brands/coingecko/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/coingecko/light.svg create mode 100644 apps/platform/public/assets/icons/brands/deepl/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/deepl/light.svg create mode 100644 apps/platform/public/assets/icons/brands/deepseek/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/deepseek/light.svg create mode 100644 apps/platform/public/assets/icons/brands/discord/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/discord/light.svg create mode 100644 apps/platform/public/assets/icons/brands/elevenlabs/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/elevenlabs/light.svg create mode 100644 apps/platform/public/assets/icons/brands/facebook/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/facebook/light.svg create mode 100644 apps/platform/public/assets/icons/brands/gemini/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/gemini/light.svg create mode 100644 apps/platform/public/assets/icons/brands/gmail/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/gmail/light.svg create mode 100644 apps/platform/public/assets/icons/brands/google_docs/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/google_docs/light.svg create mode 100644 apps/platform/public/assets/icons/brands/google_drive/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/google_drive/light.svg create mode 100644 apps/platform/public/assets/icons/brands/google_keep/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/google_keep/light.svg create mode 100644 apps/platform/public/assets/icons/brands/google_sheets/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/google_sheets/light.svg create mode 100644 apps/platform/public/assets/icons/brands/instagram/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/instagram/light.svg create mode 100644 apps/platform/public/assets/icons/brands/linkedin/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/linkedin/light.svg create mode 100644 apps/platform/public/assets/icons/brands/notion/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/notion/light.svg create mode 100644 apps/platform/public/assets/icons/brands/piapi/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/piapi/light.svg create mode 100644 apps/platform/public/assets/icons/brands/sendgrid/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/sendgrid/light.svg create mode 100644 apps/platform/public/assets/icons/brands/slack/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/slack/light.svg create mode 100644 apps/platform/public/assets/icons/brands/soundcloud/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/soundcloud/light.svg create mode 100644 apps/platform/public/assets/icons/brands/sunomusic/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/sunomusic/light.svg create mode 100644 apps/platform/public/assets/icons/brands/telegram/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/telegram/light.svg create mode 100644 apps/platform/public/assets/icons/brands/tiktok/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/tiktok/light.svg create mode 100644 apps/platform/public/assets/icons/brands/veo/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/veo/light.svg create mode 100644 apps/platform/public/assets/icons/brands/whatsapp/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/whatsapp/light.svg create mode 100644 apps/platform/public/assets/icons/brands/x/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/x/light.svg create mode 100644 apps/platform/public/assets/icons/brands/youtube/dark.svg create mode 100644 apps/platform/public/assets/icons/brands/youtube/light.svg create mode 100644 apps/platform/public/assets/icons/glyphs/branch.svg create mode 100644 apps/platform/public/assets/icons/glyphs/chat.svg create mode 100644 apps/platform/public/assets/icons/glyphs/clock.svg create mode 100644 apps/platform/public/assets/icons/glyphs/eye.svg create mode 100644 apps/platform/public/assets/icons/glyphs/file.svg create mode 100644 apps/platform/public/assets/icons/glyphs/film.svg create mode 100644 apps/platform/public/assets/icons/glyphs/folder.svg create mode 100644 apps/platform/public/assets/icons/glyphs/image.svg create mode 100644 apps/platform/public/assets/icons/glyphs/list.svg create mode 100644 apps/platform/public/assets/icons/glyphs/music.svg create mode 100644 apps/platform/public/assets/icons/glyphs/note.svg create mode 100644 apps/platform/public/assets/icons/glyphs/organize.svg create mode 100644 apps/platform/public/assets/icons/glyphs/pencil.svg create mode 100644 apps/platform/public/assets/icons/glyphs/play.svg create mode 100644 apps/platform/public/assets/icons/glyphs/plus.svg create mode 100644 apps/platform/public/assets/icons/glyphs/record.svg create mode 100644 apps/platform/public/assets/icons/glyphs/search.svg create mode 100644 apps/platform/public/assets/icons/glyphs/send.svg create mode 100644 apps/platform/public/assets/icons/glyphs/speaker.svg create mode 100644 apps/platform/public/assets/icons/glyphs/story.svg create mode 100644 apps/platform/public/assets/icons/glyphs/table.svg create mode 100644 apps/platform/public/assets/icons/glyphs/target.svg create mode 100644 apps/platform/public/assets/icons/glyphs/translate.svg create mode 100644 apps/platform/public/assets/icons/glyphs/trash.svg create mode 100644 apps/platform/public/assets/icons/glyphs/trending.svg create mode 100644 apps/platform/public/assets/icons/glyphs/upload.svg diff --git a/apps/platform/public/assets/icons/brands/airtable/dark.svg b/apps/platform/public/assets/icons/brands/airtable/dark.svg new file mode 100644 index 0000000..d665cd6 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/airtable/dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/airtable/light.svg b/apps/platform/public/assets/icons/brands/airtable/light.svg new file mode 100644 index 0000000..d665cd6 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/airtable/light.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/anthropic/dark.svg b/apps/platform/public/assets/icons/brands/anthropic/dark.svg new file mode 100644 index 0000000..53e1e5a --- /dev/null +++ b/apps/platform/public/assets/icons/brands/anthropic/dark.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/anthropic/light.svg b/apps/platform/public/assets/icons/brands/anthropic/light.svg new file mode 100644 index 0000000..53e1e5a --- /dev/null +++ b/apps/platform/public/assets/icons/brands/anthropic/light.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/chatgpt/dark.svg b/apps/platform/public/assets/icons/brands/chatgpt/dark.svg new file mode 100644 index 0000000..a9dd189 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/chatgpt/dark.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/chatgpt/light.svg b/apps/platform/public/assets/icons/brands/chatgpt/light.svg new file mode 100644 index 0000000..f1593b5 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/chatgpt/light.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/coingecko/dark.svg b/apps/platform/public/assets/icons/brands/coingecko/dark.svg new file mode 100644 index 0000000..63bb108 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/coingecko/dark.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/coingecko/light.svg b/apps/platform/public/assets/icons/brands/coingecko/light.svg new file mode 100644 index 0000000..63bb108 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/coingecko/light.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/deepl/dark.svg b/apps/platform/public/assets/icons/brands/deepl/dark.svg new file mode 100644 index 0000000..de6bbeb --- /dev/null +++ b/apps/platform/public/assets/icons/brands/deepl/dark.svg @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/deepl/light.svg b/apps/platform/public/assets/icons/brands/deepl/light.svg new file mode 100644 index 0000000..de6bbeb --- /dev/null +++ b/apps/platform/public/assets/icons/brands/deepl/light.svg @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/deepseek/dark.svg b/apps/platform/public/assets/icons/brands/deepseek/dark.svg new file mode 100644 index 0000000..023fc12 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/deepseek/dark.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/deepseek/light.svg b/apps/platform/public/assets/icons/brands/deepseek/light.svg new file mode 100644 index 0000000..023fc12 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/deepseek/light.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/discord/dark.svg b/apps/platform/public/assets/icons/brands/discord/dark.svg new file mode 100644 index 0000000..72e1638 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/discord/dark.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/discord/light.svg b/apps/platform/public/assets/icons/brands/discord/light.svg new file mode 100644 index 0000000..72e1638 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/discord/light.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/elevenlabs/dark.svg b/apps/platform/public/assets/icons/brands/elevenlabs/dark.svg new file mode 100644 index 0000000..ae54e65 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/elevenlabs/dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/platform/public/assets/icons/brands/elevenlabs/light.svg b/apps/platform/public/assets/icons/brands/elevenlabs/light.svg new file mode 100644 index 0000000..2632523 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/elevenlabs/light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/platform/public/assets/icons/brands/facebook/dark.svg b/apps/platform/public/assets/icons/brands/facebook/dark.svg new file mode 100644 index 0000000..1e5d22b --- /dev/null +++ b/apps/platform/public/assets/icons/brands/facebook/dark.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/facebook/light.svg b/apps/platform/public/assets/icons/brands/facebook/light.svg new file mode 100644 index 0000000..1e5d22b --- /dev/null +++ b/apps/platform/public/assets/icons/brands/facebook/light.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/gemini/dark.svg b/apps/platform/public/assets/icons/brands/gemini/dark.svg new file mode 100644 index 0000000..50d172c --- /dev/null +++ b/apps/platform/public/assets/icons/brands/gemini/dark.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/gemini/light.svg b/apps/platform/public/assets/icons/brands/gemini/light.svg new file mode 100644 index 0000000..50d172c --- /dev/null +++ b/apps/platform/public/assets/icons/brands/gemini/light.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/gmail/dark.svg b/apps/platform/public/assets/icons/brands/gmail/dark.svg new file mode 100644 index 0000000..1f48bd4 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/gmail/dark.svg @@ -0,0 +1,22 @@ + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/gmail/light.svg b/apps/platform/public/assets/icons/brands/gmail/light.svg new file mode 100644 index 0000000..1f48bd4 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/gmail/light.svg @@ -0,0 +1,22 @@ + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/google_docs/dark.svg b/apps/platform/public/assets/icons/brands/google_docs/dark.svg new file mode 100644 index 0000000..c372163 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/google_docs/dark.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/apps/platform/public/assets/icons/brands/google_docs/light.svg b/apps/platform/public/assets/icons/brands/google_docs/light.svg new file mode 100644 index 0000000..c372163 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/google_docs/light.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/apps/platform/public/assets/icons/brands/google_drive/dark.svg b/apps/platform/public/assets/icons/brands/google_drive/dark.svg new file mode 100644 index 0000000..2422dd2 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/google_drive/dark.svg @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/google_drive/light.svg b/apps/platform/public/assets/icons/brands/google_drive/light.svg new file mode 100644 index 0000000..2422dd2 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/google_drive/light.svg @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/google_keep/dark.svg b/apps/platform/public/assets/icons/brands/google_keep/dark.svg new file mode 100644 index 0000000..85e7ff4 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/google_keep/dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/google_keep/light.svg b/apps/platform/public/assets/icons/brands/google_keep/light.svg new file mode 100644 index 0000000..85e7ff4 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/google_keep/light.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/google_sheets/dark.svg b/apps/platform/public/assets/icons/brands/google_sheets/dark.svg new file mode 100644 index 0000000..509e400 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/google_sheets/dark.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/apps/platform/public/assets/icons/brands/google_sheets/light.svg b/apps/platform/public/assets/icons/brands/google_sheets/light.svg new file mode 100644 index 0000000..509e400 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/google_sheets/light.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/apps/platform/public/assets/icons/brands/instagram/dark.svg b/apps/platform/public/assets/icons/brands/instagram/dark.svg new file mode 100644 index 0000000..a29bb97 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/instagram/dark.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/instagram/light.svg b/apps/platform/public/assets/icons/brands/instagram/light.svg new file mode 100644 index 0000000..a29bb97 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/instagram/light.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/linkedin/dark.svg b/apps/platform/public/assets/icons/brands/linkedin/dark.svg new file mode 100644 index 0000000..d32f684 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/linkedin/dark.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/apps/platform/public/assets/icons/brands/linkedin/light.svg b/apps/platform/public/assets/icons/brands/linkedin/light.svg new file mode 100644 index 0000000..d32f684 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/linkedin/light.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/apps/platform/public/assets/icons/brands/notion/dark.svg b/apps/platform/public/assets/icons/brands/notion/dark.svg new file mode 100644 index 0000000..50511cf --- /dev/null +++ b/apps/platform/public/assets/icons/brands/notion/dark.svg @@ -0,0 +1,20 @@ + + + + + diff --git a/apps/platform/public/assets/icons/brands/notion/light.svg b/apps/platform/public/assets/icons/brands/notion/light.svg new file mode 100644 index 0000000..50511cf --- /dev/null +++ b/apps/platform/public/assets/icons/brands/notion/light.svg @@ -0,0 +1,20 @@ + + + + + diff --git a/apps/platform/public/assets/icons/brands/piapi/dark.svg b/apps/platform/public/assets/icons/brands/piapi/dark.svg new file mode 100644 index 0000000..14658ea --- /dev/null +++ b/apps/platform/public/assets/icons/brands/piapi/dark.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/piapi/light.svg b/apps/platform/public/assets/icons/brands/piapi/light.svg new file mode 100644 index 0000000..14658ea --- /dev/null +++ b/apps/platform/public/assets/icons/brands/piapi/light.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/sendgrid/dark.svg b/apps/platform/public/assets/icons/brands/sendgrid/dark.svg new file mode 100644 index 0000000..e2eeb79 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/sendgrid/dark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/sendgrid/light.svg b/apps/platform/public/assets/icons/brands/sendgrid/light.svg new file mode 100644 index 0000000..e2eeb79 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/sendgrid/light.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/slack/dark.svg b/apps/platform/public/assets/icons/brands/slack/dark.svg new file mode 100644 index 0000000..7c6e1e4 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/slack/dark.svg @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/slack/light.svg b/apps/platform/public/assets/icons/brands/slack/light.svg new file mode 100644 index 0000000..7c6e1e4 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/slack/light.svg @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/soundcloud/dark.svg b/apps/platform/public/assets/icons/brands/soundcloud/dark.svg new file mode 100644 index 0000000..44430de --- /dev/null +++ b/apps/platform/public/assets/icons/brands/soundcloud/dark.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/soundcloud/light.svg b/apps/platform/public/assets/icons/brands/soundcloud/light.svg new file mode 100644 index 0000000..44430de --- /dev/null +++ b/apps/platform/public/assets/icons/brands/soundcloud/light.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/sunomusic/dark.svg b/apps/platform/public/assets/icons/brands/sunomusic/dark.svg new file mode 100644 index 0000000..a53a3eb --- /dev/null +++ b/apps/platform/public/assets/icons/brands/sunomusic/dark.svg @@ -0,0 +1,8 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/sunomusic/light.svg b/apps/platform/public/assets/icons/brands/sunomusic/light.svg new file mode 100644 index 0000000..f5bfe0d --- /dev/null +++ b/apps/platform/public/assets/icons/brands/sunomusic/light.svg @@ -0,0 +1,8 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/telegram/dark.svg b/apps/platform/public/assets/icons/brands/telegram/dark.svg new file mode 100644 index 0000000..628b14b --- /dev/null +++ b/apps/platform/public/assets/icons/brands/telegram/dark.svg @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/telegram/light.svg b/apps/platform/public/assets/icons/brands/telegram/light.svg new file mode 100644 index 0000000..628b14b --- /dev/null +++ b/apps/platform/public/assets/icons/brands/telegram/light.svg @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/tiktok/dark.svg b/apps/platform/public/assets/icons/brands/tiktok/dark.svg new file mode 100644 index 0000000..ba7c518 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/tiktok/dark.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/apps/platform/public/assets/icons/brands/tiktok/light.svg b/apps/platform/public/assets/icons/brands/tiktok/light.svg new file mode 100644 index 0000000..147cc2c --- /dev/null +++ b/apps/platform/public/assets/icons/brands/tiktok/light.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/apps/platform/public/assets/icons/brands/veo/dark.svg b/apps/platform/public/assets/icons/brands/veo/dark.svg new file mode 100644 index 0000000..50d172c --- /dev/null +++ b/apps/platform/public/assets/icons/brands/veo/dark.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/veo/light.svg b/apps/platform/public/assets/icons/brands/veo/light.svg new file mode 100644 index 0000000..50d172c --- /dev/null +++ b/apps/platform/public/assets/icons/brands/veo/light.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/whatsapp/dark.svg b/apps/platform/public/assets/icons/brands/whatsapp/dark.svg new file mode 100644 index 0000000..e9fc431 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/whatsapp/dark.svg @@ -0,0 +1,24 @@ + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/whatsapp/light.svg b/apps/platform/public/assets/icons/brands/whatsapp/light.svg new file mode 100644 index 0000000..e9fc431 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/whatsapp/light.svg @@ -0,0 +1,24 @@ + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/x/dark.svg b/apps/platform/public/assets/icons/brands/x/dark.svg new file mode 100644 index 0000000..485af02 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/x/dark.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/x/light.svg b/apps/platform/public/assets/icons/brands/x/light.svg new file mode 100644 index 0000000..5b8eebd --- /dev/null +++ b/apps/platform/public/assets/icons/brands/x/light.svg @@ -0,0 +1,6 @@ + + + diff --git a/apps/platform/public/assets/icons/brands/youtube/dark.svg b/apps/platform/public/assets/icons/brands/youtube/dark.svg new file mode 100644 index 0000000..b1f3c71 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/youtube/dark.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/brands/youtube/light.svg b/apps/platform/public/assets/icons/brands/youtube/light.svg new file mode 100644 index 0000000..b1f3c71 --- /dev/null +++ b/apps/platform/public/assets/icons/brands/youtube/light.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/apps/platform/public/assets/icons/glyphs/branch.svg b/apps/platform/public/assets/icons/glyphs/branch.svg new file mode 100644 index 0000000..70c7df8 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/branch.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/chat.svg b/apps/platform/public/assets/icons/glyphs/chat.svg new file mode 100644 index 0000000..1cf9585 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/clock.svg b/apps/platform/public/assets/icons/glyphs/clock.svg new file mode 100644 index 0000000..a139253 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/clock.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/eye.svg b/apps/platform/public/assets/icons/glyphs/eye.svg new file mode 100644 index 0000000..2415ab4 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/eye.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/file.svg b/apps/platform/public/assets/icons/glyphs/file.svg new file mode 100644 index 0000000..101aa72 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/file.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/film.svg b/apps/platform/public/assets/icons/glyphs/film.svg new file mode 100644 index 0000000..90f12af --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/film.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/folder.svg b/apps/platform/public/assets/icons/glyphs/folder.svg new file mode 100644 index 0000000..b6ffb4f --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/folder.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/image.svg b/apps/platform/public/assets/icons/glyphs/image.svg new file mode 100644 index 0000000..bf27ae6 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/image.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/list.svg b/apps/platform/public/assets/icons/glyphs/list.svg new file mode 100644 index 0000000..4f55502 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/list.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/music.svg b/apps/platform/public/assets/icons/glyphs/music.svg new file mode 100644 index 0000000..40ad103 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/music.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/note.svg b/apps/platform/public/assets/icons/glyphs/note.svg new file mode 100644 index 0000000..2232f32 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/note.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/organize.svg b/apps/platform/public/assets/icons/glyphs/organize.svg new file mode 100644 index 0000000..d68a44a --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/organize.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/pencil.svg b/apps/platform/public/assets/icons/glyphs/pencil.svg new file mode 100644 index 0000000..6a752d6 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/pencil.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/play.svg b/apps/platform/public/assets/icons/glyphs/play.svg new file mode 100644 index 0000000..3658d0d --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/play.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/plus.svg b/apps/platform/public/assets/icons/glyphs/plus.svg new file mode 100644 index 0000000..6a7f549 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/record.svg b/apps/platform/public/assets/icons/glyphs/record.svg new file mode 100644 index 0000000..2ea8eba --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/record.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/search.svg b/apps/platform/public/assets/icons/glyphs/search.svg new file mode 100644 index 0000000..5e48905 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/send.svg b/apps/platform/public/assets/icons/glyphs/send.svg new file mode 100644 index 0000000..eaed8b9 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/send.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/speaker.svg b/apps/platform/public/assets/icons/glyphs/speaker.svg new file mode 100644 index 0000000..ee12a9d --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/speaker.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/story.svg b/apps/platform/public/assets/icons/glyphs/story.svg new file mode 100644 index 0000000..687269a --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/story.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/table.svg b/apps/platform/public/assets/icons/glyphs/table.svg new file mode 100644 index 0000000..adc057d --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/table.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/target.svg b/apps/platform/public/assets/icons/glyphs/target.svg new file mode 100644 index 0000000..e32c9e9 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/target.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/translate.svg b/apps/platform/public/assets/icons/glyphs/translate.svg new file mode 100644 index 0000000..4c225fe --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/translate.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/trash.svg b/apps/platform/public/assets/icons/glyphs/trash.svg new file mode 100644 index 0000000..523aca7 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/trash.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/trending.svg b/apps/platform/public/assets/icons/glyphs/trending.svg new file mode 100644 index 0000000..d9f2f29 --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/trending.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/platform/public/assets/icons/glyphs/upload.svg b/apps/platform/public/assets/icons/glyphs/upload.svg new file mode 100644 index 0000000..b6a117a --- /dev/null +++ b/apps/platform/public/assets/icons/glyphs/upload.svg @@ -0,0 +1,3 @@ + + + From 565dac9184ff4d4d7a5a14405fda6d606ae7337e Mon Sep 17 00:00:00 2001 From: enteresanlikk <23117378+enteresanlikk@users.noreply.github.com> Date: Sat, 8 Aug 2026 01:58:45 +0300 Subject: [PATCH 3/6] feat(flow-editor): render nodes from what the API declares --- .../shared/advanced-debounced-textarea.tsx | 8 +- .../src/components/shared/app-pagination.tsx | 16 +- .../src/components/shared/custom-icons.tsx | 8 +- .../src/components/shared/search-input.tsx | 12 +- .../api-keys/components/api-key-tab.tsx | 8 +- .../features/api-keys/hooks/use-api-keys.ts | 12 +- .../features/api-keys/services/api-keys.ts | 4 +- .../providers/change-email-form.tsx | 16 +- .../providers/change-password-form.tsx | 18 +- .../providers/email-linked-account.tsx | 10 +- .../providers/password-linked-account.tsx | 28 +- .../providers/password-register-form.tsx | 34 +- .../features/auth/hooks/use-oauth-login.ts | 2 +- .../components/credential-form-dialog.tsx | 36 +- .../components/credential-popover.tsx | 14 +- .../credentials/components/credential-tab.tsx | 19 +- .../credentials/hooks/use-credentials.ts | 24 +- .../credentials/services/credentials.ts | 4 +- .../flow-editor/components/flow-canvas.tsx | 59 +++- .../components/flow-drag-preview.tsx | 9 +- .../flow-editor/components/flow-run.tsx | 4 +- .../flow-editor/components/flow-sidebar.tsx | 173 +++++----- .../flow-editor/components/flow-toolbar.tsx | 4 +- .../components/node-settings-panel.tsx | 20 +- .../components/schema-field-renderer.tsx | 40 ++- .../flow-editor/hooks/use-flow-drag.ts | 33 +- .../flow-editor/hooks/use-flow-nodes.ts | 106 ++++-- .../flow-editor/hooks/use-navigation-guard.ts | 12 +- .../flow-editor/icons/brand-icons.tsx | 119 ------- .../src/features/flow-editor/icons/index.ts | 71 +--- .../flow-editor/icons/productivity-icons.tsx | 313 ----------------- .../flow-editor/icons/provider-icons.tsx | 119 +++++++ .../flow-editor/icons/social-icons.tsx | 322 ------------------ .../features/flow-editor/nodes/annotation.tsx | 61 ++-- .../features/flow-editor/nodes/core-node.tsx | 92 +++-- .../flow-editor/nodes/node-handles.tsx | 73 ++++ .../flow-editor/nodes/starter-node.tsx | 79 ----- .../src/features/flow-editor/types.ts | 16 +- .../generation/components/ai-chat-sheet.tsx | 8 +- .../components/workflow-preview.tsx | 2 +- .../generation/hooks/use-chat-session.ts | 24 +- .../generation/services/generation.ts | 20 +- .../mcp/components/mcp-server-card.tsx | 50 ++- .../components/mcp-server-detail-dialog.tsx | 29 +- .../mcp/components/mcp-tool-schema.tsx | 8 +- .../platform/src/features/mcp/services/mcp.ts | 3 + .../features/navigation/components/logo.tsx | 10 +- .../navigation/components/main-nav.tsx | 4 +- .../components/organization-switcher.tsx | 10 +- .../navigation/components/user-nav.tsx | 2 +- .../components/notifications-bell.tsx | 12 +- .../components/notifications-tab.tsx | 4 +- .../notifications/hooks/use-notifications.ts | 28 +- .../notifications/services/notifications.ts | 4 +- .../components/create-organization-form.tsx | 12 +- .../components/history/output-renderer.tsx | 68 +++- .../components/history/run-step-detail.tsx | 12 +- .../settings/settings-basic-info.tsx | 4 +- .../hooks/use-organization-credentials.ts | 12 +- .../hooks/use-organization-members.ts | 16 +- .../organizations/services/organizations.ts | 16 +- .../components/profile-section.tsx | 19 +- .../features/sessions/components/sessions.tsx | 33 +- .../features/theme/components/mode-toggle.tsx | 4 +- .../tour/components/tour-component.tsx | 10 +- .../features/tour/components/tour-overlay.tsx | 5 +- .../workflows/hooks/use-executions.ts | 8 +- .../features/workflows/hooks/use-triggers.ts | 12 +- .../features/workflows/hooks/use-workflows.ts | 16 +- .../features/workflows/services/executions.ts | 4 +- .../features/workflows/services/triggers.ts | 4 +- .../features/workflows/services/workflows.ts | 12 +- apps/platform/src/hooks/use-copy.ts | 12 +- .../src/hooks/use-organization-events.ts | 8 +- .../src/layouts/organization-layout.tsx | 4 +- .../platform/src/layouts/protected-layout.tsx | 4 +- apps/platform/src/lib/flow-categories.ts | 42 +-- apps/platform/src/lib/i18n.ts | 5 +- apps/platform/src/lib/token-manager.ts | 8 +- apps/platform/src/lib/ws-manager.ts | 16 +- .../pages/auth/email-change/confirm/page.tsx | 8 +- .../src/pages/auth/forgot-password/page.tsx | 12 +- apps/platform/src/pages/auth/login/page.tsx | 22 +- .../pages/auth/magic-link/callback/page.tsx | 8 +- .../src/pages/auth/reset-password/page.tsx | 29 +- .../src/pages/auth/verify-email/page.tsx | 8 +- apps/platform/src/pages/mcp/page.tsx | 30 +- .../[organizationId]/(root)/page.tsx | 16 +- .../[organizationId]/history/[runId]/page.tsx | 20 +- .../[organizationId]/history/page.tsx | 12 +- .../[organizationId]/run/[id]/page.tsx | 4 +- .../settings/members/page.tsx | 8 +- .../[organizationId]/triggers/page.tsx | 28 +- .../[organizationId]/welcome/page.tsx | 7 +- apps/platform/src/styles/categories.css | 39 +++ apps/platform/src/styles/index.css | 1 + 96 files changed, 1320 insertions(+), 1514 deletions(-) delete mode 100644 apps/platform/src/features/flow-editor/icons/brand-icons.tsx delete mode 100644 apps/platform/src/features/flow-editor/icons/productivity-icons.tsx create mode 100644 apps/platform/src/features/flow-editor/icons/provider-icons.tsx delete mode 100644 apps/platform/src/features/flow-editor/icons/social-icons.tsx create mode 100644 apps/platform/src/features/flow-editor/nodes/node-handles.tsx delete mode 100644 apps/platform/src/features/flow-editor/nodes/starter-node.tsx create mode 100644 apps/platform/src/styles/categories.css diff --git a/apps/platform/src/components/shared/advanced-debounced-textarea.tsx b/apps/platform/src/components/shared/advanced-debounced-textarea.tsx index 1aa5732..4cf5576 100644 --- a/apps/platform/src/components/shared/advanced-debounced-textarea.tsx +++ b/apps/platform/src/components/shared/advanced-debounced-textarea.tsx @@ -22,14 +22,18 @@ export const AdvancedDebouncedTextarea = React.memo( useEffect(() => { return () => { - if (timeoutRef.current) clearTimeout(timeoutRef.current) + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } } }, []) const handleChange = (e: React.ChangeEvent) => { const newText = e.target.value setText(newText) - if (timeoutRef.current) clearTimeout(timeoutRef.current) + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } timeoutRef.current = setTimeout(() => { props.onChange?.(newText) }, 500) diff --git a/apps/platform/src/components/shared/app-pagination.tsx b/apps/platform/src/components/shared/app-pagination.tsx index 6e34dee..d71f177 100644 --- a/apps/platform/src/components/shared/app-pagination.tsx +++ b/apps/platform/src/components/shared/app-pagination.tsx @@ -31,11 +31,17 @@ function computePages(current: number, total: number): Array { return Array.from({ length: total }, (_, i) => i + 1) } const pages: Array = [1] - if (current > 3) pages.push('ellipsis-start') + if (current > 3) { + pages.push('ellipsis-start') + } const start = Math.max(2, current - 1) const end = Math.min(total - 1, current + 1) - for (let i = start; i <= end; i++) pages.push(i) - if (current < total - 2) pages.push('ellipsis-end') + for (let i = start; i <= end; i++) { + pages.push(i) + } + if (current < total - 2) { + pages.push('ellipsis-end') + } pages.push(total) return pages } @@ -58,7 +64,9 @@ export function AppPagination({ [currentPage, totalPages], ) - if (total <= 0) return null + if (total <= 0) { + return null + } return (
diff --git a/apps/platform/src/components/shared/custom-icons.tsx b/apps/platform/src/components/shared/custom-icons.tsx index 87f1305..8ec504b 100644 --- a/apps/platform/src/components/shared/custom-icons.tsx +++ b/apps/platform/src/components/shared/custom-icons.tsx @@ -44,10 +44,14 @@ const flowIconCache = new Map() export const FlowIcon = (seed: string) => { const cached = flowIconCache.get(seed) - if (cached) return cached + if (cached) { + return cached + } let hash = 0 - for (let i = 0; i < seed.length; i++) hash += seed.charCodeAt(i) + for (let i = 0; i < seed.length; i++) { + hash += seed.charCodeAt(i) + } const baseColor = FLOW_ICON_COLORS[hash % FLOW_ICON_COLORS.length] const dataUrl = generate(seed, { baseColor }).toDataUrl() diff --git a/apps/platform/src/components/shared/search-input.tsx b/apps/platform/src/components/shared/search-input.tsx index 2a254d1..9c3ab50 100644 --- a/apps/platform/src/components/shared/search-input.tsx +++ b/apps/platform/src/components/shared/search-input.tsx @@ -34,12 +34,16 @@ export function SearchInput({ useEffect(() => { return () => { - if (timeoutRef.current) clearTimeout(timeoutRef.current) + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } } }, []) const fireSearch = (next: string) => { - if (timeoutRef.current) clearTimeout(timeoutRef.current) + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } timeoutRef.current = setTimeout(() => { onSearchRef.current(next) }, debounceMs) @@ -53,7 +57,9 @@ export function SearchInput({ const handleClear = () => { setValue('') - if (timeoutRef.current) clearTimeout(timeoutRef.current) + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } onSearchRef.current('') } diff --git a/apps/platform/src/features/api-keys/components/api-key-tab.tsx b/apps/platform/src/features/api-keys/components/api-key-tab.tsx index 11391eb..b5b8aea 100644 --- a/apps/platform/src/features/api-keys/components/api-key-tab.tsx +++ b/apps/platform/src/features/api-keys/components/api-key-tab.tsx @@ -73,7 +73,9 @@ const APIKeyTab = ({ ) const handleCreate = async () => { - if (!selectedScopes.length) return + if (!selectedScopes.length) { + return + } const created = await onCreate({ name: newKeyName || t('ui.text.apiKey', 'API Key'), scopes: selectedScopes, @@ -195,7 +197,9 @@ const APIKeyTab = ({ open={createOpen} onOpenChange={(open) => { setCreateOpen(open) - if (!open) resetCreateForm() + if (!open) { + resetCreateForm() + } }} > diff --git a/apps/platform/src/features/api-keys/hooks/use-api-keys.ts b/apps/platform/src/features/api-keys/hooks/use-api-keys.ts index 3a70151..d89a66d 100644 --- a/apps/platform/src/features/api-keys/hooks/use-api-keys.ts +++ b/apps/platform/src/features/api-keys/hooks/use-api-keys.ts @@ -58,7 +58,9 @@ export function useOrganizationApiKeys( const handleCreate = useCallback( async (payload: unknown) => { - if (!organizationId) return null + if (!organizationId) { + return null + } const res = await apiKeysService.createOrganizationApiKey( organizationId, payload as never, @@ -74,7 +76,9 @@ export function useOrganizationApiKeys( const handleRegenerate = useCallback( async (apiKeyId: string, payload: unknown) => { - if (!organizationId) return null + if (!organizationId) { + return null + } const res = await apiKeysService.regenerateOrganizationApiKey( organizationId, apiKeyId, @@ -91,7 +95,9 @@ export function useOrganizationApiKeys( const handleDelete = useCallback( async (apiKeyId: string) => { - if (!organizationId) return + if (!organizationId) { + return + } await apiKeysService.deleteOrganizationApiKey(organizationId, apiKeyId) await revalidate() }, diff --git a/apps/platform/src/features/api-keys/services/api-keys.ts b/apps/platform/src/features/api-keys/services/api-keys.ts index dc77dba..6b4ebdd 100644 --- a/apps/platform/src/features/api-keys/services/api-keys.ts +++ b/apps/platform/src/features/api-keys/services/api-keys.ts @@ -7,7 +7,9 @@ const getOrganizationApiKeys = async ( const params = new URLSearchParams() params.set('offset', String(offset)) params.set('limit', String(limit)) - if (query) params.set('query', query) + if (query) { + params.set('query', query) + } return await platformApi.get( `/organizations/${organizationId}/api-keys?${params.toString()}`, ) diff --git a/apps/platform/src/features/auth/components/providers/change-email-form.tsx b/apps/platform/src/features/auth/components/providers/change-email-form.tsx index 4f1fac8..5d9087e 100644 --- a/apps/platform/src/features/auth/components/providers/change-email-form.tsx +++ b/apps/platform/src/features/auth/components/providers/change-email-form.tsx @@ -26,7 +26,9 @@ const ChangeEmailForm = () => { } const clearError = (key: string) => { - if (!errors[key]) return + if (!errors[key]) { + return + } setErrors((prev) => { const next = { ...prev } delete next[key] @@ -37,7 +39,7 @@ const ChangeEmailForm = () => { const validate = () => { const next: Record = {} if (!newEmail.trim()) { - next.newEmail = t('ui.text.emailRequired', { ns: 'ui' }) + next.newEmail = t('ui.text.emailRequired') } if (!currentPassword) { next.currentPassword = t('ui.text.current_password_required') @@ -48,7 +50,9 @@ const ChangeEmailForm = () => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() - if (!validate()) return + if (!validate()) { + return + } setSubmitting(true) try { const response = await authService.changeEmail({ @@ -87,7 +91,7 @@ const ChangeEmailForm = () => { variant="ghost" size="sm" onClick={reset} - aria-label={t('ui.text.cancel', { ns: 'ui' })} + aria-label={t('ui.text.cancel')} disabled={submitting} > @@ -158,9 +162,7 @@ const ChangeEmailForm = () => { size="sm" tabIndex={-1} aria-label={ - showPassword - ? t('ui.text.hide', { ns: 'ui' }) - : t('ui.text.show', { ns: 'ui' }) + showPassword ? t('ui.text.hide') : t('ui.text.show') } className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 p-0" onClick={() => setShowPassword((s) => !s)} diff --git a/apps/platform/src/features/auth/components/providers/change-password-form.tsx b/apps/platform/src/features/auth/components/providers/change-password-form.tsx index 7532609..95e3644 100644 --- a/apps/platform/src/features/auth/components/providers/change-password-form.tsx +++ b/apps/platform/src/features/auth/components/providers/change-password-form.tsx @@ -32,7 +32,9 @@ const ChangePasswordForm = () => { } const clearError = (key: string) => { - if (!errors[key]) return + if (!errors[key]) { + return + } setErrors((prev) => { const next = { ...prev } delete next[key] @@ -57,7 +59,9 @@ const ChangePasswordForm = () => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() - if (!validate()) return + if (!validate()) { + return + } setSubmitting(true) try { const response = await authService.changePassword({ @@ -96,7 +100,7 @@ const ChangePasswordForm = () => { variant="ghost" size="sm" onClick={reset} - aria-label={t('ui.text.cancel', { ns: 'ui' })} + aria-label={t('ui.text.cancel')} disabled={submitting} > @@ -141,7 +145,7 @@ const ChangePasswordForm = () => { /> { clearError('confirmPassword') @@ -208,11 +212,7 @@ const PasswordField = ({ variant="ghost" size="sm" tabIndex={-1} - aria-label={ - show - ? t('ui.text.hide', { ns: 'ui' }) - : t('ui.text.show', { ns: 'ui' }) - } + aria-label={show ? t('ui.text.hide') : t('ui.text.show')} className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 p-0" onClick={toggleShow} > diff --git a/apps/platform/src/features/auth/components/providers/email-linked-account.tsx b/apps/platform/src/features/auth/components/providers/email-linked-account.tsx index 11b806a..6da9372 100644 --- a/apps/platform/src/features/auth/components/providers/email-linked-account.tsx +++ b/apps/platform/src/features/auth/components/providers/email-linked-account.tsx @@ -28,7 +28,7 @@ const EmailLinkedAccount = ({ onLinked }: Props) => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!email.trim()) { - setError(t('ui.text.emailRequired', { ns: 'ui' })) + setError(t('ui.text.emailRequired')) return } setSubmitting(true) @@ -55,7 +55,7 @@ const EmailLinkedAccount = ({ onLinked }: Props) => {
- {t('ui.text.emailAddress', { ns: 'ui' })} + {t('ui.text.emailAddress')}
{t('ui.text.add_email_description')} @@ -67,7 +67,7 @@ const EmailLinkedAccount = ({ onLinked }: Props) => { variant="ghost" size="sm" onClick={reset} - aria-label={t('ui.text.cancel', { ns: 'ui' })} + aria-label={t('ui.text.cancel')} disabled={submitting} > @@ -94,9 +94,7 @@ const EmailLinkedAccount = ({ onLinked }: Props) => { {expanded && !sent && (
- + { } const clearError = (key: string) => { - if (!errors[key]) return + if (!errors[key]) { + return + } setErrors((prev) => { const next = { ...prev } delete next[key] @@ -54,7 +56,9 @@ const PasswordLinkedAccount = ({ onLinked }: Props) => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() - if (!validate()) return + if (!validate()) { + return + } setSubmitting(true) try { const response = await authService.setPassword({ password }) @@ -78,9 +82,7 @@ const PasswordLinkedAccount = ({ onLinked }: Props) => {
-
- {t('ui.text.password', { ns: 'ui' })} -
+
{t('ui.text.password')}
{t('ui.text.set_password_description')}
@@ -91,7 +93,7 @@ const PasswordLinkedAccount = ({ onLinked }: Props) => { variant="ghost" size="sm" onClick={reset} - aria-label={t('ui.text.cancel', { ns: 'ui' })} + aria-label={t('ui.text.cancel')} disabled={submitting} > @@ -106,9 +108,7 @@ const PasswordLinkedAccount = ({ onLinked }: Props) => { {expanded && (
- +
{ size="sm" tabIndex={-1} aria-label={ - showPassword - ? t('ui.text.hide', { ns: 'ui' }) - : t('ui.text.show', { ns: 'ui' }) + showPassword ? t('ui.text.hide') : t('ui.text.show') } className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 p-0" onClick={() => setShowPassword((s) => !s)} @@ -151,7 +149,7 @@ const PasswordLinkedAccount = ({ onLinked }: Props) => {
{ size="sm" tabIndex={-1} aria-label={ - showConfirmPassword - ? t('ui.text.hide', { ns: 'ui' }) - : t('ui.text.show', { ns: 'ui' }) + showConfirmPassword ? t('ui.text.hide') : t('ui.text.show') } className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 p-0" onClick={() => setShowConfirmPassword((s) => !s)} diff --git a/apps/platform/src/features/auth/components/providers/password-register-form.tsx b/apps/platform/src/features/auth/components/providers/password-register-form.tsx index aa88d76..b4155c0 100644 --- a/apps/platform/src/features/auth/components/providers/password-register-form.tsx +++ b/apps/platform/src/features/auth/components/providers/password-register-form.tsx @@ -25,7 +25,9 @@ const PasswordRegisterForm = ({ onSubmitted }: Props) => { const validate = () => { const next: Record = {} - if (!email.trim()) next.email = t('ui.text.emailRequired', { ns: 'ui' }) + if (!email.trim()) { + next.email = t('ui.text.emailRequired') + } if (password.length < MIN_PASSWORD_LENGTH) { next.password = t('ui.text.password_too_short') } @@ -38,7 +40,9 @@ const PasswordRegisterForm = ({ onSubmitted }: Props) => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() - if (!validate()) return + if (!validate()) { + return + } setSubmitting(true) try { const response = await authService.register({ email, password }) @@ -63,7 +67,9 @@ const PasswordRegisterForm = ({ onSubmitted }: Props) => { } const clearError = (key: string) => { - if (!errors[key]) return + if (!errors[key]) { + return + } setErrors((prev) => { const next = { ...prev } delete next[key] @@ -74,9 +80,7 @@ const PasswordRegisterForm = ({ onSubmitted }: Props) => { return (
- + { )}
- +
{ variant="ghost" size="sm" tabIndex={-1} - aria-label={ - showPassword - ? t('ui.text.hide', { ns: 'ui' }) - : t('ui.text.show', { ns: 'ui' }) - } + aria-label={showPassword ? t('ui.text.hide') : t('ui.text.show')} className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 p-0" onClick={() => setShowPassword((s) => !s)} > @@ -138,9 +136,7 @@ const PasswordRegisterForm = ({ onSubmitted }: Props) => { )}
- +
{ size="sm" tabIndex={-1} aria-label={ - showConfirmPassword - ? t('ui.text.hide', { ns: 'ui' }) - : t('ui.text.show', { ns: 'ui' }) + showConfirmPassword ? t('ui.text.hide') : t('ui.text.show') } className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 p-0" onClick={() => setShowConfirmPassword((s) => !s)} diff --git a/apps/platform/src/features/auth/hooks/use-oauth-login.ts b/apps/platform/src/features/auth/hooks/use-oauth-login.ts index a7eaf10..537e151 100644 --- a/apps/platform/src/features/auth/hooks/use-oauth-login.ts +++ b/apps/platform/src/features/auth/hooks/use-oauth-login.ts @@ -23,6 +23,6 @@ export function useOAuthLogin(provider: string, mode: string) { url.searchParams.set('state', encodedState) window.location.href = url.toString() } - } catch (error) {} + } catch {} }, [provider, mode]) } diff --git a/apps/platform/src/features/credentials/components/credential-form-dialog.tsx b/apps/platform/src/features/credentials/components/credential-form-dialog.tsx index 930165c..606e196 100644 --- a/apps/platform/src/features/credentials/components/credential-form-dialog.tsx +++ b/apps/platform/src/features/credentials/components/credential-form-dialog.tsx @@ -42,7 +42,9 @@ const CredentialFormDialog = ({ const clearError = (key: string) => { setErrors((prev) => { - if (!prev[key]) return prev + if (!prev[key]) { + return prev + } const next = { ...prev } delete next[key] return next @@ -50,7 +52,9 @@ const CredentialFormDialog = ({ } useEffect(() => { - if (!open) setErrors({}) + if (!open) { + setErrors({}) + } }, [open]) useEffect(() => { @@ -67,20 +71,28 @@ const CredentialFormDialog = ({ ) { return selectedSecret.secretDetails.data[schema.key] } - if (schema?.default) return schema.default + if (schema?.default) { + return schema.default + } return '' } const validate = () => { const errs: Record = {} const titleValue = (selectedSecret?.label || '').toString().trim() - if (!titleValue) errs['s-t'] = true + if (!titleValue) { + errs['s-t'] = true + } if (!usePlatformCredential && selectedSecret?.provider) { for (const schema of selectedSecret.provider) { - if (schema.hidden || schema.readOnly || !schema.required) continue + if (schema.hidden || schema.readOnly || !schema.required) { + continue + } const value = (getFieldValue(schema) || '').toString().trim() - if (!value) errs[schema.key] = true + if (!value) { + errs[schema.key] = true + } } } @@ -89,13 +101,17 @@ const CredentialFormDialog = ({ } const handleSave = () => { - if (!validate()) return + if (!validate()) { + return + } saveCredentials() onOpenChange(false) } const handleAuthorize = () => { - if (!validate()) return + if (!validate()) { + return + } authorizeOAuth2() } @@ -205,7 +221,9 @@ const CredentialFormDialog = ({ if (!usePlatformCredential && selectedSecret?.provider) { for (let i = 0; i < selectedSecret.provider.length; i++) { const schema = selectedSecret.provider[i] - if (schema.hidden) continue + if (schema.hidden) { + continue + } let currentValue = '' if (selectedSecret.data && selectedSecret.data[schema.key]) { diff --git a/apps/platform/src/features/credentials/components/credential-popover.tsx b/apps/platform/src/features/credentials/components/credential-popover.tsx index ef9200f..95bd14d 100644 --- a/apps/platform/src/features/credentials/components/credential-popover.tsx +++ b/apps/platform/src/features/credentials/components/credential-popover.tsx @@ -21,13 +21,19 @@ const CredentialPopover = ({ const handleOpenChange = (next: boolean) => { setOpen(next) - if (!next) setQuery('') + if (!next) { + setQuery('') + } } const filteredSecrets = useMemo(() => { - if (!secrets) return [] + if (!secrets) { + return [] + } const q = query.trim().toLowerCase() - if (!q) return secrets + if (!q) { + return secrets + } return secrets.filter((secret: any) => t(secret.name).toLowerCase().includes(q), ) @@ -49,7 +55,7 @@ const CredentialPopover = ({ onSelect(secretCopy) }} > - + {t(secret.name)} , ) diff --git a/apps/platform/src/features/credentials/components/credential-tab.tsx b/apps/platform/src/features/credentials/components/credential-tab.tsx index 335ef94..4ec5ea0 100644 --- a/apps/platform/src/features/credentials/components/credential-tab.tsx +++ b/apps/platform/src/features/credentials/components/credential-tab.tsx @@ -91,12 +91,18 @@ const CredentialTab = ({ if (secretDetails) { setSelectedSecret((prev: any) => { - if (!prev) return prev + if (!prev) { + return prev + } const next = { ...prev } next.secretDetails = secretDetails next.sourceType = secretDetails.sourceType - if (secretDetails.data) next.data = { ...secretDetails.data } - if (secretDetails.title) next.label = secretDetails.title + if (secretDetails.data) { + next.data = { ...secretDetails.data } + } + if (secretDetails.title) { + next.label = secretDetails.title + } return next }) if (secretDetails.sourceType === 'platform') { @@ -111,8 +117,11 @@ const CredentialTab = ({ ns.label = t(ns.name) const c = JSON.parse(JSON.stringify(ns)) setSelectedSecret(c) - if (w) w(true) - else setOpen(true) + if (w) { + w(true) + } else { + setOpen(true) + } } const saveCredentials = async () => { diff --git a/apps/platform/src/features/credentials/hooks/use-credentials.ts b/apps/platform/src/features/credentials/hooks/use-credentials.ts index 0e61fff..9600518 100644 --- a/apps/platform/src/features/credentials/hooks/use-credentials.ts +++ b/apps/platform/src/features/credentials/hooks/use-credentials.ts @@ -3,13 +3,13 @@ import useSWR, { useSWRConfig } from 'swr' import credentialsService from '@/features/credentials/services/credentials' import { organizationsService } from '@/features/organizations' import { nodeEngineService } from '@/features/flow-editor' -import { secretIcons } from '@/features/flow-editor/icons' -import { resolveIconKey } from '@/features/flow-editor/hooks/use-flow-nodes' -import { useThemeStore } from '@/stores/theme-store' +import { useIconResolver } from '@/features/flow-editor/icons' import { unwrap } from '@/lib/swr' const schemaToFields = (schema: any) => { - if (!schema || !schema.properties) return [] + if (!schema || !schema.properties) { + return [] + } const required = new Set(schema.required ?? []) return Object.entries(schema.properties).map( ([key, value]: [string, any]) => ({ @@ -45,7 +45,7 @@ export function useCredentials( isUserMode: boolean, ) { const hasAccess = isUserMode || !!organizationId - const mode = useThemeStore((s) => s.getMode()) + const resolveIcon = useIconResolver() const [pagination, setPagination] = useState({ offset: 0, @@ -101,19 +101,17 @@ export function useCredentials( }) return sa.map((secret: any) => { const credential = credentialMap[secret.key] - const iconKey = credential ? resolveIconKey(credential.icon, mode) : '' return { ...secret, - icon: iconKey ? secretIcons[iconKey] : null, + icon: (credential && resolveIcon(credential.icon)) ?? null, isSupportPlatform: credential?.isSupportPlatform || false, sourceType: secret.sourceType || 'owner', } }) - }, [ca, sa, mode]) + }, [ca, sa, resolveIcon]) const secrets = useMemo(() => { return ca.map((c) => { - const iconKey = resolveIconKey(c.icon, mode) return { id: c.id, name: c.name, @@ -121,10 +119,10 @@ export function useCredentials( isOAuth2: c.isOAuth2, isSupportPlatform: c.isSupportPlatform, provider: schemaToFields(c.schema), - icon: iconKey ? secretIcons[iconKey] : undefined, + icon: resolveIcon(c.icon), } }) - }, [ca, mode]) + }, [ca, resolveIcon]) const fetchCredentials = useCallback((offset = 0, limit = 10, query = '') => { setPagination({ offset, limit, query }) @@ -136,7 +134,9 @@ export function useCredentials( const getSecretDetails = useCallback( async (secretId: string) => { - if (!hasAccess || !secretId) return null + if (!hasAccess || !secretId) { + return null + } try { const response = isUserMode ? await credentialsService.getUserCredentialById(secretId) diff --git a/apps/platform/src/features/credentials/services/credentials.ts b/apps/platform/src/features/credentials/services/credentials.ts index 589987b..9c3ffaf 100644 --- a/apps/platform/src/features/credentials/services/credentials.ts +++ b/apps/platform/src/features/credentials/services/credentials.ts @@ -8,7 +8,9 @@ const getUserCredentials = async ({ const params = new URLSearchParams() params.set('offset', String(offset)) params.set('limit', String(limit)) - if (query) params.set('query', query) + if (query) { + params.set('query', query) + } return await platformApi.get(`/users/me/credentials?${params.toString()}`) } diff --git a/apps/platform/src/features/flow-editor/components/flow-canvas.tsx b/apps/platform/src/features/flow-editor/components/flow-canvas.tsx index 8906ec6..774bc60 100644 --- a/apps/platform/src/features/flow-editor/components/flow-canvas.tsx +++ b/apps/platform/src/features/flow-editor/components/flow-canvas.tsx @@ -1,5 +1,4 @@ import coreNode from '@/features/flow-editor/nodes/core-node' -import starterNode from '@/features/flow-editor/nodes/starter-node' import coreEdge from '@/features/flow-editor/nodes/core-edge' import coreGroup from '@/features/flow-editor/nodes/core-group' import { useBreadcrumbStore } from '@/features/flow-editor/stores/breadcrumb-store' @@ -53,11 +52,12 @@ const defaultNodes = [ title: 'ui.text.starterNode', description: 'ui.text.starterNodeDescription', hide_input: true, - hide_left: true, tags: ['starter'], category: 'system', + inputs: [], + outputs: [{ key: 'out' }], }, - type: 'starter', + type: 'core', nodeId: 'system_starter', deletable: false, }, @@ -65,7 +65,7 @@ const defaultNodes = [ const nodeTypes = { core: coreNode, - starter: starterNode, + starter: coreNode, group: coreGroup, annotation: Annotation, } @@ -119,7 +119,7 @@ const FlowCanvas = ({ const { t } = useTranslation() // Hooks - const { apiNodes, nodeList, isLoading } = useNodeEngineNodes() + const { apiNodes, providerList, isLoading } = useNodeEngineNodes() const { triggerVariables } = useTriggerVariables() const { createContextNode } = useFlowNodes(apiNodes) @@ -179,11 +179,20 @@ const FlowCanvas = ({ label: node.name, description: node.description, type: node.category, + icon: node.icon, subCategory: node.subCategory, tags: node.tags, isComingSoon: node.isComingSoon || false, })) - setSelectedCategory(filtered) + setSelectedCategory( + query + ? { + label: t('ui.text.searchResults'), + nodes: filtered, + isSearch: true, + } + : null, + ) }, [apiNodes, t], ) @@ -197,6 +206,9 @@ const FlowCanvas = ({ description: apiNode?.description || '', tags: apiNode?.tags || [], category: apiNode?.category || '', + icon: apiNode?.icon, + inputs: apiNode?.inputs, + outputs: apiNode?.outputs, } }, [apiNodes], @@ -289,12 +301,21 @@ const FlowCanvas = ({ useEffect(() => { if (initialFlow) { setFunctions() + return + } + if (apiNodes.length === 0) { + return } + setNodes((current) => + current.map((n) => ({ ...n, data: { ...n.data, ...buildNodeData(n) } })), + ) }, [initialFlow, apiNodes]) const applyGeneratedFlow = useCallback( (flow) => { - if (!flow || apiNodes.length === 0) return + if (!flow || apiNodes.length === 0) { + return + } const eds = flow.edges.map((e) => ({ ...e })) const nds = flow.nodes.map((n) => { const data = buildNodeData(n) @@ -316,7 +337,9 @@ const FlowCanvas = ({ ) useEffect(() => { - if (generatedFlow) applyGeneratedFlow(generatedFlow) + if (generatedFlow) { + applyGeneratedFlow(generatedFlow) + } }, [generatedFlow, applyGeneratedFlow]) const handleApplyWorkflow = useCallback( @@ -329,6 +352,9 @@ const FlowCanvas = ({ // Node settings panel const onNodeClick = useCallback((_event, node) => { + if (node.type === 'annotation') { + return + } setSelectedNodeId(node.id) setChatOpen(false) }, []) @@ -343,13 +369,17 @@ const FlowCanvas = ({ ) const selectedNodeSchema = useMemo(() => { - if (!selectedNode) return [] + if (!selectedNode) { + return [] + } const apiNode = apiNodes.find((a) => a.id === selectedNode.nodeId) return apiNode?.schema || [] }, [selectedNode, apiNodes]) const selectedNodeHasNaturalLanguage = useMemo(() => { - if (!selectedNode) return undefined + if (!selectedNode) { + return undefined + } const apiNode = apiNodes.find((a) => a.id === selectedNode.nodeId) return apiNode?.hasNaturalLanguage }, [selectedNode, apiNodes]) @@ -385,7 +415,9 @@ const FlowCanvas = ({ const handleSetChatOpen = useCallback((v) => { setChatOpen(v) - if (v) setSelectedNodeId(null) + if (v) { + setSelectedNodeId(null) + } }, []) const nextStepInit = (fn) => { @@ -435,7 +467,7 @@ const FlowCanvas = ({ setSidebarOpen={setSidebarOpen} selectedCategory={selectedCategory} setSelectedCategory={setSelectedCategory} - nodeList={nodeList} + providerList={providerList} apiNodes={apiNodes} dragging={dragging} startDrag={startDrag} @@ -453,6 +485,9 @@ const FlowCanvas = ({ openApiSheet={ initialFlow?.id ? () => setApiSheetOpen(true) : undefined } + nodes={nodes} + apiNodes={apiNodes} + organizationId={organizationId} /> { - if (!dragging || !previewData) return null + if (!dragging || !previewData) { + return null + } const pref = getCategoryPrefs(previewData.category) const Icon = pref.icon return (
-
+
diff --git a/apps/platform/src/features/flow-editor/components/flow-run.tsx b/apps/platform/src/features/flow-editor/components/flow-run.tsx index 3b4b866..acfca0c 100644 --- a/apps/platform/src/features/flow-editor/components/flow-run.tsx +++ b/apps/platform/src/features/flow-editor/components/flow-run.tsx @@ -18,7 +18,9 @@ const FlowRun = ({ flow, setFlow }) => { const steps = [] for (let i = 0; i < flow.nodes.length; i++) { const node = flow.nodes[i] - if (node.type === 'starter') continue + if (node.type === 'starter') { + continue + } const runnerKey = node.nodeId?.replaceAll('.', '_') if (runnerKey && Runner[runnerKey]) { node.runner = Runner[runnerKey] diff --git a/apps/platform/src/features/flow-editor/components/flow-sidebar.tsx b/apps/platform/src/features/flow-editor/components/flow-sidebar.tsx index 60d6d53..150a4f3 100644 --- a/apps/platform/src/features/flow-editor/components/flow-sidebar.tsx +++ b/apps/platform/src/features/flow-editor/components/flow-sidebar.tsx @@ -1,14 +1,17 @@ import { + ChevronLeft, PanelLeft, PanelLeftClose, Search, - ChevronLeft, + SearchX, Sparkles, + StickyNote, } from 'lucide-react' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { GhostInput } from '@/components/shared/ghost-input' import { getCategoryPrefs } from '@/lib/flow-categories' +import { useIconResolver } from '@/features/flow-editor/icons' import { usePlatformFeatures } from '@/features/platform' import { useTranslation } from 'react-i18next' @@ -18,7 +21,7 @@ const FlowSidebar = ({ setSidebarOpen, selectedCategory, setSelectedCategory, - nodeList, + providerList, dragging, startDrag, annotationDrag, @@ -27,11 +30,12 @@ const FlowSidebar = ({ }) => { const { t } = useTranslation() const { workflowsGenerationEnabled } = usePlatformFeatures() + const resolveIcon = useIconResolver() const renderNodeCard = (item, key) => { const name = item.label || item.name const prefs = getCategoryPrefs(item.type || item.category) - const Icon = prefs.icon + const Icon = resolveIcon(item.icon) ?? prefs.icon const isDisabled = item.isComingSoon const cursorClass = isDisabled ? 'cursor-not-allowed' @@ -47,59 +51,51 @@ const FlowSidebar = ({
!isDisabled && startDrag(e, item.id)} role="button" tabIndex={isDisabled ? -1 : 0} aria-label={ariaLabel} aria-disabled={isDisabled} > -
- -
+
) } const renderAnnotationCard = () => { - const annotationPrefs = getCategoryPrefs('system') - const AnnotationIcon = annotationPrefs?.icon - return (
-
- -
{t('ui.text.annotationNode')}
-
-
- {t('ui.text.addAnnotationToFlow')} +
) @@ -107,82 +103,76 @@ const FlowSidebar = ({ const renderNodeList = () => { if (selectedCategory) { - const isFlat = Array.isArray(selectedCategory) - const flatItems = isFlat - ? selectedCategory - : Object.values(selectedCategory).flat() - const hasSystem = flatItems.some((cat: any) => cat.type === 'system') + const items = selectedCategory.nodes ?? [] + const hasSystem = items.some((node: any) => node.type === 'system') - let body: React.ReactNode - - if (isFlat) { - body = ( - <> - {selectedCategory.map((item, j) => renderNodeCard(item, j))} - {hasSystem && renderAnnotationCard()} - - ) - } else { - const subEntries = Object.entries( - selectedCategory as Record, - ) - body = ( - <> - {subEntries.map(([sub, items]) => ( -
- {sub && ( -
- {t(sub)} -
- )} - {items.map((item, j) => renderNodeCard(item, `${sub}-${j}`))} -
- ))} - {hasSystem && renderAnnotationCard()} - + if (items.length === 0) { + return ( +
  • +
  • ) } return ( -
  • - {body} +
  • + {items.map((item: any, j: number) => renderNodeCard(item, j))} + {hasSystem && renderAnnotationCard()}
  • ) } - const categories = Object.keys(nodeList).map((category, i) => { - const prefs = getCategoryPrefs(category) - const Icon = prefs.icon - const categoryLabel = prefs.labelKey ? t(prefs.labelKey) : category + const providers = providerList.map((provider: any) => { + const prefs = getCategoryPrefs(provider.nodes[0]?.type ?? '') + const Icon = resolveIcon(provider.icon) ?? prefs.icon return (
    setSelectedCategory(nodeList[category])} - className={`flex flex-col items-center justify-center rounded-lg transition-colors w-full min-h-33 cursor-pointer bg-current/10 dark:hover:bg-current/50 hover:bg-current/20 gap-3`} - style={{ color: prefs.color }} - data-tour={category === 'system' ? 'system-category' : undefined} + key={provider.key} + onClick={() => + setSelectedCategory({ + label: provider.label, + nodes: provider.nodes, + }) + } + style={{ borderColor: prefs.color }} + className="bg-card hover:bg-accent flex w-full cursor-pointer items-center gap-3 rounded-md border px-3 py-2.5 transition-colors" + data-tour={provider.key === 'system' ? 'system-category' : undefined} role="button" tabIndex={0} - aria-label={categoryLabel} + aria-label={provider.label} > -
    ) }) return ( -
  • - {categories} +
  • + {providers}
  • ) } - if (previewMode) return null + if (previewMode) { + return null + } return (
    @@ -219,7 +209,7 @@ const FlowSidebar = ({
    -
      +
    -
    - {(() => { - const flat = Array.isArray(selectedCategory) - ? selectedCategory - : Object.values( - selectedCategory as Record, - ).flat() - return flat[0]?.type ?? 'Results' - })()} -
    +
    {selectedCategory.label}
    ) : ( <> diff --git a/apps/platform/src/features/flow-editor/components/flow-toolbar.tsx b/apps/platform/src/features/flow-editor/components/flow-toolbar.tsx index 26a1b9f..244756a 100644 --- a/apps/platform/src/features/flow-editor/components/flow-toolbar.tsx +++ b/apps/platform/src/features/flow-editor/components/flow-toolbar.tsx @@ -17,7 +17,9 @@ const FlowToolbar = ({ }) => { const { t } = useTranslation() - if (previewMode) return null + if (previewMode) { + return null + } return (
    diff --git a/apps/platform/src/features/flow-editor/components/node-settings-panel.tsx b/apps/platform/src/features/flow-editor/components/node-settings-panel.tsx index b1f45bb..721feff 100644 --- a/apps/platform/src/features/flow-editor/components/node-settings-panel.tsx +++ b/apps/platform/src/features/flow-editor/components/node-settings-panel.tsx @@ -7,12 +7,14 @@ import { Label } from '@/components/ui/label' import { Switch } from '@/components/ui/switch' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { usePlatformFeatures } from '@/features/platform' +import { useIconResolver } from '@/features/flow-editor/icons' import { AdvancedDebouncedTextarea } from '@/components/shared/advanced-debounced-textarea' import { SchemaFieldRenderer } from '@/features/flow-editor/components/schema-field-renderer' import { DataSourcePopover } from '@/features/flow-editor/components/data-source-popover' import { flowCategoryPreferences } from '@/lib/flow-categories' import type { FlowNode, + IconSource, NodeSettings, SchemaField, } from '@/features/flow-editor/types' @@ -53,7 +55,9 @@ export function NodeSettingsPanel({ const initial: Record = {} const existing = node.parameters || {} for (const field of schema || []) { - if (field.hidden) continue + if (field.hidden) { + continue + } initial[field.key] = existing[field.key] ?? field.default ?? '' } return initial @@ -69,7 +73,9 @@ export function NodeSettingsPanel({ const initial: Record = {} const existing = node.parameters || {} for (const field of schema || []) { - if (field.hidden) continue + if (field.hidden) { + continue + } initial[field.key] = existing[field.key] ?? field.default ?? '' } setParameters(initial) @@ -100,7 +106,8 @@ export function NodeSettingsPanel({ return flowCategoryPreferences[category] }, [node.data.category]) - const Icon = prefs.icon + const resolveIcon = useIconResolver() + const Icon = resolveIcon(node.data?.icon as IconSource) ?? prefs.icon const visibleSchema = useMemo( () => (schema || []).filter((f) => !f.hidden), @@ -123,11 +130,8 @@ export function NodeSettingsPanel({ > {/* Header */}
    -
    - {Icon && } +
    + {Icon && }
    diff --git a/apps/platform/src/features/flow-editor/components/schema-field-renderer.tsx b/apps/platform/src/features/flow-editor/components/schema-field-renderer.tsx index a5eac32..1dde804 100644 --- a/apps/platform/src/features/flow-editor/components/schema-field-renderer.tsx +++ b/apps/platform/src/features/flow-editor/components/schema-field-renderer.tsx @@ -49,15 +49,25 @@ const isMultilineFormat = (format?: string) => format === 'multiline' || format === 'textarea' || format === 'multi-line' const inputTypeFor = (field: SchemaField) => { - if (field.writeOnly) return 'password' - if (field.format === 'email') return 'email' - if (field.format === 'uri' || field.format === 'url') return 'url' + if (field.writeOnly) { + return 'password' + } + if (field.format === 'email') { + return 'email' + } + if (field.format === 'uri' || field.format === 'url') { + return 'url' + } return 'text' } const stringifyComplex = (val: any) => { - if (val === undefined || val === null) return '' - if (typeof val === 'string') return val + if (val === undefined || val === null) { + return '' + } + if (typeof val === 'string') { + return val + } try { return JSON.stringify(val, null, 2) } catch { @@ -68,15 +78,23 @@ const stringifyComplex = (val: any) => { const PRIMITIVE_ITEM_TYPES = new Set(['string', 'number', 'integer', 'boolean']) const isPrimitiveItemSchema = (items: any) => { - if (!items || typeof items !== 'object') return true + if (!items || typeof items !== 'object') { + return true + } const type = items.type - if (type === undefined) return true + if (type === undefined) { + return true + } return PRIMITIVE_ITEM_TYPES.has(type) } const defaultForItemType = (type?: string) => { - if (type === 'boolean') return false - if (type === 'number' || type === 'integer') return 0 + if (type === 'boolean') { + return false + } + if (type === 'number' || type === 'integer') { + return 0 + } return '' } @@ -222,7 +240,9 @@ export function SchemaFieldRenderer({ }: SchemaFieldRendererProps) { const { t } = useTranslation() - if (field.hidden) return null + if (field.hidden) { + return null + } const labelText = t(field.title || field.key, field.title || field.key) const isReadOnly = !!field.readOnly diff --git a/apps/platform/src/features/flow-editor/hooks/use-flow-drag.ts b/apps/platform/src/features/flow-editor/hooks/use-flow-drag.ts index 481868e..34117f3 100644 --- a/apps/platform/src/features/flow-editor/hooks/use-flow-drag.ts +++ b/apps/platform/src/features/flow-editor/hooks/use-flow-drag.ts @@ -2,6 +2,8 @@ import { useState, useRef } from 'react' import { useReactFlow } from '@xyflow/react' import type { FlowNode, + IconSource, + NodeHandle, NodeOutputType, SchemaField, SetFlowNodes, @@ -17,6 +19,10 @@ interface DragItem { name: string description: string category: string + icon?: IconSource + inputs?: NodeHandle[] + outputs?: NodeHandle[] + subCategory?: string tags?: string[] isComingSoon?: boolean outputTypes?: NodeOutputType[] @@ -55,11 +61,15 @@ export function useFlowDrag({ const pane = document.querySelector( '.react-flow__pane.draggable', ) as HTMLElement - if (pane) pane.style.cursor = 'grab' + if (pane) { + pane.style.cursor = 'grab' + } document.removeEventListener('mousemove', onMouseMove) document.removeEventListener('mouseup', onMouseUp) - if (!dragData.current) return + if (!dragData.current) { + return + } const position = screenToFlowPosition({ x: e.clientX + 100, y: e.clientY, @@ -72,6 +82,9 @@ export function useFlowDrag({ tags: dragData.current.tags, title: dragData.current.name, category: dragData.current.category, + icon: dragData.current.icon, + inputs: dragData.current.inputs, + outputs: dragData.current.outputs, }, instruction: undefined, parameters: {}, @@ -95,12 +108,16 @@ export function useFlowDrag({ const pane = document.querySelector( '.react-flow__pane.draggable', ) as HTMLElement - if (pane) pane.style.cursor = 'grab' + if (pane) { + pane.style.cursor = 'grab' + } document.removeEventListener('mousemove', onMouseMove) document.removeEventListener('mouseup', onMouseUpAnnotation) - if (!dragData.current) return + if (!dragData.current) { + return + } const position = screenToFlowPosition({ x: e.clientX + 100, @@ -139,7 +156,9 @@ export function useFlowDrag({ const pane = document.querySelector( '.react-flow__pane.draggable', ) as HTMLElement - if (pane) pane.style.cursor = 'grabbing' + if (pane) { + pane.style.cursor = 'grabbing' + } document.addEventListener('mousemove', onMouseMove) document.addEventListener('mouseup', onMouseUp) } @@ -161,7 +180,9 @@ export function useFlowDrag({ const pane = document.querySelector( '.react-flow__pane.draggable', ) as HTMLElement - if (pane) pane.style.cursor = 'grabbing' + if (pane) { + pane.style.cursor = 'grabbing' + } document.addEventListener('mousemove', onMouseMove) document.addEventListener('mouseup', onMouseUpAnnotation) diff --git a/apps/platform/src/features/flow-editor/hooks/use-flow-nodes.ts b/apps/platform/src/features/flow-editor/hooks/use-flow-nodes.ts index 69654f9..9584fa1 100644 --- a/apps/platform/src/features/flow-editor/hooks/use-flow-nodes.ts +++ b/apps/platform/src/features/flow-editor/hooks/use-flow-nodes.ts @@ -2,11 +2,11 @@ import { useCallback, useEffect, useMemo, useRef } from 'react' import useSWR from 'swr' import nodeEngineService from '@/features/flow-editor/services/node-engine' import { useTranslation } from 'react-i18next' -import { useThemeStore } from '@/stores/theme-store' import type { ContextMenuItem, FlowEdge, FlowNode, + IconSource, NodeEngineNode, NodeOutputType, RendererNode, @@ -15,18 +15,13 @@ import type { SchemaField, } from '@/features/flow-editor/types' -export const resolveIconKey = (icon: any, mode: 'light' | 'dark'): string => { - if (!icon) return '' - if (typeof icon === 'string') return icon - if (mode === 'dark' && icon.dark) return icon.dark - return icon.light ?? icon.dark ?? '' -} - const normalizeKey = (value?: string) => (value ?? '').toLowerCase().replace(/\s+/g, '') const inputSchemaToFields = (schema: any): SchemaField[] => { - if (!schema?.properties) return [] + if (!schema?.properties) { + return [] + } const required = new Set(schema.required ?? []) return Object.entries(schema.properties).map( ([key, value]: [string, any]) => ({ @@ -53,7 +48,9 @@ const collectOutputPaths = ( prefix: string, acc: NodeOutputType[], ) => { - if (!node || typeof node !== 'object') return + if (!node || typeof node !== 'object') { + return + } if (node.type === 'object' && node.properties) { for (const [key, value] of Object.entries(node.properties)) { @@ -85,7 +82,9 @@ const collectOutputPaths = ( } const outputSchemaToTypes = (schema: any): NodeOutputType[] => { - if (!schema || typeof schema !== 'object') return [] + if (!schema || typeof schema !== 'object') { + return [] + } const acc: NodeOutputType[] = [] collectOutputPaths(schema, '', acc) return acc @@ -95,13 +94,19 @@ const buildNodeList = (nodes: ResolvedNode[]): RendererNodeMap => { const r: RendererNodeMap = {} for (const item of nodes) { const subCategory = item.subCategory ?? '' - if (!r[item.category]) r[item.category] = {} - if (!r[item.category][subCategory]) r[item.category][subCategory] = [] + if (!r[item.category]) { + r[item.category] = {} + } + if (!r[item.category][subCategory]) { + r[item.category][subCategory] = [] + } const labels: string[] = [] if (item.schema && item.schema.length > 0) { for (const field of item.schema) { if (Array.isArray(field.enum)) { - for (const opt of field.enum) labels.push(String(opt)) + for (const opt of field.enum) { + labels.push(String(opt)) + } } } } @@ -110,6 +115,9 @@ const buildNodeList = (nodes: ResolvedNode[]): RendererNodeMap => { label: item.name, description: item.description, type: item.category, + icon: item.icon, + inputs: item.inputs, + outputs: item.outputs, subCategory: item.subCategory, actions: labels, tags: item.tags, @@ -120,11 +128,45 @@ const buildNodeList = (nodes: ResolvedNode[]): RendererNodeMap => { return r } +const buildProviderList = (nodes: ResolvedNode[]) => { + const groups = new Map< + string, + { key: string; label: string; icon: IconSource; nodes: RendererNode[] } + >() + + for (const item of nodes) { + const label = item.provider || item.subCategory || '' + const key = item.subCategory || normalizeKey(label) + if (!groups.has(key)) { + groups.set(key, { + key, + label, + icon: { brand: item.icon?.brand }, + nodes: [], + }) + } + groups.get(key)!.nodes.push({ + id: item.id, + label: item.name, + description: item.description, + type: item.category, + icon: item.icon, + inputs: item.inputs, + outputs: item.outputs, + subCategory: item.subCategory, + actions: [], + tags: item.tags, + isComingSoon: item.isComingSoon ?? false, + }) + } + + return [...groups.values()].sort((a, b) => a.label.localeCompare(b.label)) +} + const NODES_KEY = 'node-engine-nodes' const TRIGGER_VARIABLES_KEY = 'node-engine-trigger-variables' export function useNodeEngineNodes() { - const mode = useThemeStore((s) => s.getMode()) const { data, isLoading } = useSWR(NODES_KEY, async () => { const response = await nodeEngineService.getNodes() return (response.data as NodeEngineNode[]) || [] @@ -136,9 +178,12 @@ export function useNodeEngineNodes() { id: item.id ?? '', name: item.name ?? '', description: item.description ?? '', - icon: resolveIconKey(item.icon, mode), + icon: item.icon, + inputs: item.inputs, + outputs: item.outputs, category: normalizeKey(item.categories?.[0]), subCategory: normalizeKey(item.subCategories?.[0]), + provider: item.subCategories?.[0] ?? '', tags: item.tags ?? [], supportedCredentials: item.supportedCredentials, schema: inputSchemaToFields(item.inputSchema), @@ -147,11 +192,12 @@ export function useNodeEngineNodes() { isComingSoon: item.isComingSoon, hasNaturalLanguage: item.hasNaturalLanguage, })), - [data, mode], + [data], ) const nodeList = useMemo(() => buildNodeList(apiNodes), [apiNodes]) + const providerList = useMemo(() => buildProviderList(apiNodes), [apiNodes]) - return { apiNodes, nodeList, isLoading } + return { apiNodes, nodeList, providerList, isLoading } } export function useTriggerVariables() { @@ -159,7 +205,9 @@ export function useTriggerVariables() { TRIGGER_VARIABLES_KEY, async () => { const response = await nodeEngineService.getTriggerVariables() - if (response.isSuccess && response.data) return response.data + if (response.isSuccess && response.data) { + return response.data + } return [] }, ) @@ -195,7 +243,9 @@ export function useFlowNodes(apiNodes: ResolvedNode[] = []) { from?: SourceRef, apiNodesArg?: ResolvedNode[], ): ContextMenuItem[] | undefined => { - if (node.id === '0') return undefined + if (node.id === '0') { + return undefined + } let contextMenu: ContextMenuItem[] = [] const ref = node.id @@ -204,7 +254,9 @@ export function useFlowNodes(apiNodes: ResolvedNode[] = []) { const apiNode = apiOriginRef.current.find( (n) => n.id === sourceNode.nodeId, ) - if (apiNode === undefined) return contextMenu + if (apiNode === undefined) { + return contextMenu + } const outputs = apiNode.outputTypes ?? [] for (let j = 0; j < outputs.length; j++) { @@ -237,15 +289,21 @@ export function useFlowNodes(apiNodes: ResolvedNode[] = []) { const edges = source.edges.filter((e) => e.target === ref) for (let i = 0; i < edges.length; i++) { const edge = edges[i] - if (edge.source === '0') continue + if (edge.source === '0') { + continue + } const sourceNode = source.nodes.find((n) => n.id === edge.source) - if (!sourceNode) continue + if (!sourceNode) { + continue + } const currentApiNodes = apiNodesArg || apiOriginRef.current const apiNode = currentApiNodes?.find( (n) => n.id === sourceNode.nodeId, ) - if (apiNode === undefined) continue + if (apiNode === undefined) { + continue + } const sourceContextMenu = sourceNode.data?.contextMenu if (sourceContextMenu && sourceContextMenu.length > 0) { contextMenu = contextMenu.concat(sourceContextMenu) diff --git a/apps/platform/src/features/flow-editor/hooks/use-navigation-guard.ts b/apps/platform/src/features/flow-editor/hooks/use-navigation-guard.ts index ffb1fef..0f28d70 100644 --- a/apps/platform/src/features/flow-editor/hooks/use-navigation-guard.ts +++ b/apps/platform/src/features/flow-editor/hooks/use-navigation-guard.ts @@ -35,8 +35,12 @@ export function useNavigationGuard({ // Link click and popstate interception useEffect(() => { - if (previewMode) return - if (!hasUnsavedChanges) return + if (previewMode) { + return + } + if (!hasUnsavedChanges) { + return + } const handleClick = (e: MouseEvent) => { const link = (e.target as HTMLElement).closest('a[href]') @@ -74,7 +78,9 @@ export function useNavigationGuard({ // beforeunload useEffect(() => { - if (previewMode) return + if (previewMode) { + return + } const handleBeforeUnload = (e: BeforeUnloadEvent) => { if (hasUnsavedChanges) { e.preventDefault() diff --git a/apps/platform/src/features/flow-editor/icons/brand-icons.tsx b/apps/platform/src/features/flow-editor/icons/brand-icons.tsx deleted file mode 100644 index 9fe2bea..0000000 --- a/apps/platform/src/features/flow-editor/icons/brand-icons.tsx +++ /dev/null @@ -1,119 +0,0 @@ -// Brand AI & Media Icons - -const AnthropicIcon = (props) => ( - - - -) - -const ChatGptIcon = (props) => ( - - - -) - -const DeepseekIcon = (props) => ( - - - -) - -const GeminiIcon = (props) => ( - - - - - - - - - - -) - -const SunoIcon = (props) => ( - - - -) - -const ElevenlabsIcon = (props) => ( - - - - -) - -const PiApiIcon = (props) => ( - - - -) - -const CreatoMateIcon = (props) => ( - - - - - -) - -const SoundCloudIcon = (props) => ( - - - -) - -export { - AnthropicIcon, - ChatGptIcon, - DeepseekIcon, - GeminiIcon, - SunoIcon, - ElevenlabsIcon, - PiApiIcon, - CreatoMateIcon, - SoundCloudIcon, -} diff --git a/apps/platform/src/features/flow-editor/icons/index.ts b/apps/platform/src/features/flow-editor/icons/index.ts index db63d0f..e1f37cc 100644 --- a/apps/platform/src/features/flow-editor/icons/index.ts +++ b/apps/platform/src/features/flow-editor/icons/index.ts @@ -1,71 +1,2 @@ -import { Send } from 'lucide-react' - -import { - AnthropicIcon, - ChatGptIcon, - DeepseekIcon, - GeminiIcon, - SunoIcon, - ElevenlabsIcon, - PiApiIcon, - CreatoMateIcon, - SoundCloudIcon, -} from './brand-icons' - -import { - XIcon, - InstagramIcon, - TiktokIcon, - FacebookIcon, - LinkedinIcon, - YouTubeIcon, - DiscordIcon, - SlackIcon, - WhatsAppIcon, - TelegramIcon, -} from './social-icons' - -import { - GmailIcon, - OutlookIcon, - GoogleDriveIcon, - GoogleSheetsIcon, - GoogleDocsIcon, - NotionIcon, - DeepLIcon, - CoinGeckoIcon, -} from './productivity-icons' - export { directionIcons } from './direction-icons' - -export const secretIcons = { - anthropic: AnthropicIcon, - chatgpt: ChatGptIcon, - deepseek: DeepseekIcon, - gemini: GeminiIcon, - gmail: GmailIcon, - outlook: OutlookIcon, - telegram: TelegramIcon, - x: XIcon, - instagram: InstagramIcon, - tiktok: TiktokIcon, - facebook: FacebookIcon, - linkedin: LinkedinIcon, - youtube: YouTubeIcon, - google_drive: GoogleDriveIcon, - google_sheets: GoogleSheetsIcon, - google_docs: GoogleDocsIcon, - sunomusic: SunoIcon, - elevenlabs: ElevenlabsIcon, - smtp: Send, - piapi: PiApiIcon, - creatomate: CreatoMateIcon, - whatsapp: WhatsAppIcon, - deepl: DeepLIcon, - coingecko: CoinGeckoIcon, - discord: DiscordIcon, - slack: SlackIcon, - notion: NotionIcon, - soundcloud: SoundCloudIcon, - veo: GeminiIcon, -} +export { useIconResolver, type ProviderIconComponent } from './provider-icons' diff --git a/apps/platform/src/features/flow-editor/icons/productivity-icons.tsx b/apps/platform/src/features/flow-editor/icons/productivity-icons.tsx deleted file mode 100644 index 61a9074..0000000 --- a/apps/platform/src/features/flow-editor/icons/productivity-icons.tsx +++ /dev/null @@ -1,313 +0,0 @@ -// Productivity & Service Icons - -const GmailIcon = (props) => ( - - - - - - - -) - -const OutlookIcon = (props) => ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -) - -const GoogleDriveIcon = (props) => ( - - - - - - - - -) - -const GoogleSheetsIcon = (props) => ( - - - - - -) - -const GoogleDocsIcon = (props) => ( - - - - - -) - -const NotionIcon = (props) => ( - - - - - -) - -const DeepLIcon = (props) => ( - - - - - - - - -) - -const CoinGeckoIcon = (props) => ( - - - - - - - - - - - - - - - - -) - -export { - GmailIcon, - OutlookIcon, - GoogleDriveIcon, - GoogleSheetsIcon, - GoogleDocsIcon, - NotionIcon, - DeepLIcon, - CoinGeckoIcon, -} diff --git a/apps/platform/src/features/flow-editor/icons/provider-icons.tsx b/apps/platform/src/features/flow-editor/icons/provider-icons.tsx new file mode 100644 index 0000000..05f86f8 --- /dev/null +++ b/apps/platform/src/features/flow-editor/icons/provider-icons.tsx @@ -0,0 +1,119 @@ +import { useCallback } from 'react' +import type { ReactNode } from 'react' + +import type { IconSource } from '@/features/flow-editor/types' +import { cn } from '@/lib/utils' +import { useThemeStore } from '@/stores/theme-store' + +const brandSource = (brand: string, mode: 'light' | 'dark') => + `/assets/icons/brands/${brand}/${mode}.svg` +const glyphSource = (glyph: string) => `/assets/icons/glyphs/${glyph}.svg` + +type IconProps = { + className?: string +} + +export type ProviderIconComponent = (props: IconProps) => ReactNode + +const builtIcons = new Map() + +const artwork = (source: string, className?: string) => ( + +) + +const badge = (glyph: string) => ( + + + +) + +const buildIcon = ( + icon: { brand?: string; glyph?: string }, + mode: 'light' | 'dark', +): ProviderIconComponent | undefined => { + if (icon.brand && icon.glyph) { + return ({ className }: IconProps) => ( + + {artwork(brandSource(icon.brand!, mode), 'size-[70%]')} + {badge(icon.glyph!)} + + ) + } + + if (icon.brand) { + return ({ className }: IconProps) => + artwork( + brandSource(icon.brand!, mode), + cn('inline-block shrink-0', className), + ) + } + + if (icon.glyph) { + return ({ className }: IconProps) => ( + + ) + } + + return undefined +} + +export const useIconResolver = () => { + const mode = useThemeStore((state) => state.getMode()) + + return useCallback( + (icon: IconSource) => { + if (!icon) { + return undefined + } + + const key = `${mode}:${icon.brand ?? ''}:${icon.glyph ?? ''}` + const existing = builtIcons.get(key) + if (existing) { + return existing + } + + const built = buildIcon(icon, mode) + if (built) { + builtIcons.set(key, built) + } + + return built + }, + [mode], + ) +} diff --git a/apps/platform/src/features/flow-editor/icons/social-icons.tsx b/apps/platform/src/features/flow-editor/icons/social-icons.tsx deleted file mode 100644 index 4d8a400..0000000 --- a/apps/platform/src/features/flow-editor/icons/social-icons.tsx +++ /dev/null @@ -1,322 +0,0 @@ -// Social Media Icons - -const XIcon = (props) => ( - - - -) - -const InstagramIcon = (props) => ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - -) - -const TiktokIcon = (props) => ( - - - - - -) - -const FacebookIcon = (props) => ( - - - - - - - - - - - - - - - - -) - -const LinkedinIcon = (props) => ( - - - - - -) - -const YouTubeIcon = (props) => ( - - - - - - - - - - - -) - -const DiscordIcon = (props) => ( - - - - - - - - - - - - - - - -) - -const SlackIcon = (props) => ( - - - - - - - - - - -) - -const WhatsAppIcon = (props) => ( - - - - - - - -) - -const TelegramIcon = (props) => ( - - - - - - - - - - -) - -export { - XIcon, - InstagramIcon, - TiktokIcon, - FacebookIcon, - LinkedinIcon, - YouTubeIcon, - DiscordIcon, - SlackIcon, - WhatsAppIcon, - TelegramIcon, -} diff --git a/apps/platform/src/features/flow-editor/nodes/annotation.tsx b/apps/platform/src/features/flow-editor/nodes/annotation.tsx index d5393a3..ddc4899 100644 --- a/apps/platform/src/features/flow-editor/nodes/annotation.tsx +++ b/apps/platform/src/features/flow-editor/nodes/annotation.tsx @@ -1,15 +1,18 @@ -import { useState, useRef, useEffect } from 'react' +import { useState, useRef, useEffect, useCallback } from 'react' import { useTranslation } from 'react-i18next' import { AnnotationNode, AnnotationNodeContent, } from '@/features/flow-editor/nodes/annotation-node' +import { useFlowSetNodes } from '@/features/flow-editor/contexts/flow-nodes-context' -const Annotation = () => { +const Annotation = ({ id, data }) => { const { t } = useTranslation() - const [content, setContent] = useState(t('ui.text.annotationPlaceholder')) + const setNodes = useFlowSetNodes() const [isEditing, setIsEditing] = useState(false) - const inputRef = useRef(null) + const inputRef = useRef(null) + + const note = data?.note ?? '' useEffect(() => { if (isEditing && inputRef.current) { @@ -17,20 +20,19 @@ const Annotation = () => { } }, [isEditing]) - const handleContentClick = () => { - setIsEditing(true) - } - - const handleInputChange = (e: React.ChangeEvent) => { - setContent(e.target.value) - } - - const handleInputBlur = () => { - setIsEditing(false) - } + const updateNote = useCallback( + (value: string) => { + setNodes((nodes) => + nodes.map((n) => + n.id === id ? { ...n, data: { ...n.data, note: value } } : n, + ), + ) + }, + [id, setNodes], + ) - const handleInputKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { + const handleInputKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Escape') { setIsEditing(false) } } @@ -38,29 +40,22 @@ const Annotation = () => { return ( setIsEditing(true)} + className="w-full cursor-text" > {isEditing ? (