diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/assets.cpp | 86 | ||||
| -rw-r--r-- | src/main_linux.cpp | 3 | ||||
| -rw-r--r-- | src/providers/DeepSeek.cpp | 121 | ||||
| -rw-r--r-- | src/providers/MailerSend.cpp | 1 | ||||
| -rw-r--r-- | src/providers/openAI.cpp | 3 | ||||
| -rw-r--r-- | src/ui/imgui_extensions.cpp | 2 | ||||
| -rw-r--r-- | src/ui/ui_invoices.cpp | 4 | ||||
| -rw-r--r-- | src/ui/ui_settings.cpp | 60 | ||||
| -rw-r--r-- | src/ui/ui_setup.cpp | 37 |
9 files changed, 147 insertions, 170 deletions
diff --git a/src/assets.cpp b/src/assets.cpp new file mode 100644 index 0000000..7ea3cdb --- /dev/null +++ b/src/assets.cpp @@ -0,0 +1,86 @@ +/* +* Copyright (c) 2025 Aldrik Ramaekers <aldrik.ramaekers@gmail.com> +* +* Permission to use, copy, modify, and/or distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include <GLFW/glfw3.h> +#include <stb/stb_image.h> + +#include "assets.hpp" +#include "strops.hpp" + +#include "assets/splash.h" + +#include "assets/logos/MailSender.h" +#include "assets/logos/Perplexity.h" +#include "assets/logos/OpenAI.h" +#include "assets/logos/Gemini.h" + +#define MAX_IMAGE_COUNT 5 + +image _image_dict[MAX_IMAGE_COUNT] = { + (image){-1, "img/splash", (const void*)img_splash, img_splash_length}, + (image){-1, "img/logos/mailsender", (const void*)img_mailsender, img_mailsender_length}, + (image){-1, "img/logos/openai", (const void*)img_openai, img_openai_length}, + (image){-1, "img/logos/perplexity", (const void*)img_perplexity, img_perplexity_length}, + (image){-1, "img/logos/gemini", (const void*)img_gemini, img_gemini_length}, +}; + +static int _load_image(image img) +{ + int width, height, channels; + unsigned char* data = stbi_load_from_memory((const stbi_uc*)img.data_start, img.data_length, &width, &height, &channels, 0); + + if (!data) + { + printf("%s\n", stbi_failure_reason()); + return 0; + } + + GLuint textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + + // Set texture parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Upload texture data + GLenum format = (channels == 4) ? GL_RGBA : GL_RGB; + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + //glGenerateMipmap(GL_TEXTURE_2D); + + // Free image memory + stbi_image_free(data); + + return textureID; +} + +s32 assets::load_image(const char* name) +{ + for (u32 i = 0; i < MAX_IMAGE_COUNT; i++) + { + if (strops::equals(_image_dict[i].name, name)) { + + if (_image_dict[i].img_id == -1) { + _image_dict[i].img_id = _load_image(_image_dict[i]); + } + return _image_dict[i].img_id; + } + } + + return 0; +}
\ No newline at end of file diff --git a/src/main_linux.cpp b/src/main_linux.cpp index b317ab1..95177ef 100644 --- a/src/main_linux.cpp +++ b/src/main_linux.cpp @@ -74,7 +74,8 @@ static void _create_window(bool is_setup_window) if (window == nullptr) return; glfwMakeContextCurrent(window); - glfwSwapInterval(1); // Enable vsync + glfwSwapInterval(1); + glfwWindowHint(GLFW_SAMPLES, 4); IMGUI_CHECKVERSION(); ImGui::CreateContext(); diff --git a/src/providers/DeepSeek.cpp b/src/providers/DeepSeek.cpp deleted file mode 100644 index 7c695fb..0000000 --- a/src/providers/DeepSeek.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* -* Copyright (c) 2025 Aldrik Ramaekers <aldrik.ramaekers@gmail.com> -* -* Permission to use, copy, modify, and/or distribute this software for any -* purpose with or without fee is hereby granted, provided that the above -* copyright notice and this permission notice appear in all copies. -* -* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#define CPPHTTPLIB_OPENSSL_SUPPORT -#include <cpp-httplib/httplib.h> - -#include "strops.hpp" -#include "memops.hpp" -#include "logger.hpp" -#include "importer.hpp" - -#define QUERY_BUFFER_SIZE 1000000 - -char* query_buffer = 0; -static bool _DeepSeek_query_with_file(const char* query, size_t query_length, const char* file_id, char** response) -{ - (void)file_id; - (void)query_length; - assert(query_buffer); - - const char *api_key = administration::get_active_ai_service().api_key_public; - - httplib::SSLClient cli("api.deepseek.com"); - //cli.enable_server_certificate_verification(false); - - //char* query_escaped = strops::prep_str_for_json(query, query_length); - //memops::unalloc(query); // TODO why?? - - size_t file_size = strops::length(query_buffer); - sprintf(query_buffer + file_size, "%s", query); - - char* query_escaped = strops::prep_str_for_json(query_buffer, strops::length(query_buffer)); - - size_t body_size = file_size + QUERY_BUFFER_SIZE; - char* body = (char*)memops::alloc(body_size); - strops::format(body, body_size, - "{\"model\":\"%s\", \"messages\": [ { \"role\": \"user\", \"content\": \"%s\" } ] }", administration::get_active_ai_service().model_name, query_escaped); - - httplib::Headers headers; - headers.insert(std::make_pair("Authorization", std::string("Bearer ") + api_key)); - headers.insert(std::make_pair("Content-Type", "application/json")); - headers.insert(std::make_pair("Accept", "application/json")); - - httplib::Result res = cli.Post("/chat/completions", headers, body, "application/json"); - memops::unalloc(body); - - if (!res || res->status != 200) { - logger::error("ERROR Failed to query API."); - logger::error(res->body.c_str()); - return 0; - } - - char* response_body = (char*)res->body.c_str(); - *response = (char*)memops::alloc(100000); - memops::zero(*response, 100000); - strops::copy(*response, response_body, 100000); - - strops::get_json_value(*response, "content", *response, 100000); - *response = strops::unprep_str_from_json(*response); - - return 1; -} - -static bool _DeepSeek_upload_file(const char* file_path, char* file_id, size_t file_id_len) -{ - (void)file_id; - (void)file_id_len; - const char *filename = strops::get_filename(file_path); - - FILE* orig_file = fopen(file_path, "r"); - if (orig_file == NULL) { - logger::error("ERROR: file to upload could not be opened."); - return 0; - } - - fseek(orig_file, 0L, SEEK_END); - long sz = ftell(orig_file); - fseek(orig_file, 0, SEEK_SET); - - size_t buffer_size = sz + QUERY_BUFFER_SIZE; - char* file_content_buffer = (char*)memops::alloc(buffer_size); - memops::zero(file_content_buffer, buffer_size); - - query_buffer = file_content_buffer; - - file_content_buffer += sprintf(file_content_buffer, "[file name]: %s\n", filename); - file_content_buffer += sprintf(file_content_buffer, "[file content begin]\n"); - - fread(file_content_buffer, sz, 1, orig_file); - file_content_buffer += sz; - - for (int i = 0; i < file_content_buffer-query_buffer; i++) if (query_buffer[i] <= 0x1f) query_buffer[i] = ' '; - - file_content_buffer += sprintf(file_content_buffer, "\n[file content end]\n"); - file_content_buffer[0] = 0; - - fclose(orig_file); - - return 1; -} - -importer::ai_provider_impl _deepseek_api_provider = { - "DeekSeek", - "deepseek-reasoner", - _DeepSeek_upload_file, - _DeepSeek_query_with_file, - 0, -};
\ No newline at end of file diff --git a/src/providers/MailerSend.cpp b/src/providers/MailerSend.cpp index 1758ac8..dbae4c6 100644 --- a/src/providers/MailerSend.cpp +++ b/src/providers/MailerSend.cpp @@ -72,5 +72,6 @@ bool _MailerSend_send_email(const char* sender, const char* recipient, const cha exporter::email_provider_impl _mailersend_api_provider = { "MailerSend", + "img/logos/mailsender", _MailerSend_send_email, };
\ No newline at end of file diff --git a/src/providers/openAI.cpp b/src/providers/openAI.cpp index c743711..5659465 100644 --- a/src/providers/openAI.cpp +++ b/src/providers/openAI.cpp @@ -269,6 +269,7 @@ static bool _openAI_get_available_models(importer::model_list_request* buffer) importer::ai_provider_impl _chatgpt_api_provider = { "OpenAI", "gpt-5-nano", + "img/logos/openai", _openAI_upload_file, _openAI_query_with_file, _openAI_batch_query_with_file, @@ -278,6 +279,7 @@ importer::ai_provider_impl _chatgpt_api_provider = { importer::ai_provider_impl _gemini_api_provider = { "Gemini", "", + "img/logos/gemini", 0, 0, 0, @@ -287,6 +289,7 @@ importer::ai_provider_impl _gemini_api_provider = { importer::ai_provider_impl _perplexity_api_provider = { "Perplexity", "", + "img/logos/perplexity", 0, 0, 0, diff --git a/src/ui/imgui_extensions.cpp b/src/ui/imgui_extensions.cpp index 4112568..e3e917c 100644 --- a/src/ui/imgui_extensions.cpp +++ b/src/ui/imgui_extensions.cpp @@ -881,7 +881,7 @@ namespace ImGui //style->WindowMinSize = ImVec2( 160, 20 ); style->FramePadding = ImVec2( 10, 5 ); - style->ItemSpacing = ImVec2( 6, 2 ); + style->ItemSpacing = ImVec2( 6, 5 ); style->ItemInnerSpacing = ImVec2( 6, 4 ); style->Alpha = 1.0f; style->DisabledAlpha = 0.45f; diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp index c222050..e6ee7d3 100644 --- a/src/ui/ui_invoices.cpp +++ b/src/ui/ui_invoices.cpp @@ -75,10 +75,10 @@ void draw_invoice_items_form(invoice* invoice, bool outgoing, bool viewing_only if (ImGui::BeginTable("TableBillingItems", 9, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { ImGui::TableSetupColumn("##actions", ImGuiTableColumnFlags_WidthFixed, 30); - ImGui::TableSetupColumn(locale::get("invoice.table.amount"), ImGuiTableColumnFlags_WidthFixed, 110); + ImGui::TableSetupColumn(locale::get("invoice.table.amount"), ImGuiTableColumnFlags_WidthFixed, 90); ImGui::TableSetupColumn(locale::get("invoice.table.description")); ImGui::TableSetupColumn(locale::get("invoice.table.price"), ImGuiTableColumnFlags_WidthFixed, 100); - ImGui::TableSetupColumn(locale::get("invoice.table.discount"), ImGuiTableColumnFlags_WidthFixed, 130); + ImGui::TableSetupColumn(locale::get("invoice.table.discount"), ImGuiTableColumnFlags_WidthFixed, 110); ImGui::TableSetupColumn(locale::get("invoice.table.net"), ImGuiTableColumnFlags_WidthFixed, 100); ImGui::TableSetupColumn(locale::get("invoice.table.tax%"), ImGuiTableColumnFlags_WidthFixed, 120); ImGui::TableSetupColumn(locale::get("invoice.table.tax"), ImGuiTableColumnFlags_WidthFixed, 100); diff --git a/src/ui/ui_settings.cpp b/src/ui/ui_settings.cpp index f0a0a53..95f24e3 100644 --- a/src/ui/ui_settings.cpp +++ b/src/ui/ui_settings.cpp @@ -16,6 +16,7 @@ #include "ui.hpp" +#include "assets.hpp" #include "strops.hpp" #include "memops.hpp" #include "locales.hpp" @@ -264,18 +265,20 @@ static void draw_ai_service_ui() if (ImGui::CollapsingHeader(locale::get("settings.services.ai_service"))) { + const char* ai_service_icons[AI_PROVIDER_END]; const char* ai_service_names[AI_PROVIDER_END]; for (u32 i = 0; i < AI_PROVIDER_END; i++) { - ai_service_names[i] = importer::get_ai_provider_implementation((ai_provider)i).provider_name; + importer::ai_provider_impl impl = importer::get_ai_provider_implementation((ai_provider)i); + ai_service_names[i] = impl.provider_name; + ai_service_icons[i] = impl.icon_name; } - if (ImGui::BeginCombo(locale::get("settings.services.ai_service.provider"), ai_service_names[new_ai_service.provider])) - { + if (ImGui::BeginCombo(locale::get("settings.services.ai_service.provider"), NULL, 1 << 20)) { for (u32 n = 0; n < AI_PROVIDER_END; n++) { + ImGui::PushID(n); bool is_selected = n == (uint32_t)new_ai_service.provider; - if (ImGui::Selectable(ai_service_names[n], is_selected)) { - + if (ImGui::Selectable("##emailProviderOption", is_selected)) { ai_service service = administration::get_ai_service((ai_provider)n); new_ai_service.provider = (ai_provider)n; strops::copy(new_ai_service.model_name, service.model_name, MAX_LEN_SHORT_DESC); @@ -284,9 +287,25 @@ static void draw_ai_service_ui() model_request = 0; set_model_on_load = strops::length(new_ai_service.model_name) == 0; } + + ImGui::SameLine(); + ImGui::Image(assets::load_image(ai_service_icons[n]), ImVec2(16, 16)); + + ImGui::SameLine(); + ImGui::Text(ai_service_names[n]); + + ImGui::PopID(); } ImGui::EndCombo(); } + + ImGui::BeginComboPreview(); + { + ImGui::Image(assets::load_image(ai_service_icons[new_ai_service.provider]), ImVec2(16, 16)); + ImGui::Text(ai_service_names[new_ai_service.provider]); + + ImGui::EndComboPreview(); + } ImGui::InputTextWithHint(locale::get("settings.services.ai_service.pubkey"), locale::get("settings.services.ai_service.pubkey"), new_ai_service.api_key_public, sizeof(new_ai_service.api_key_public)); @@ -348,6 +367,7 @@ static void draw_ai_service_ui() administration_writer::set_write_completed_event_callback(0); administration::set_active_ai_service(new_ai_service); } + ImGui::Spacing(); } } @@ -356,21 +376,40 @@ static void draw_email_service_ui() if (ImGui::CollapsingHeader(locale::get("settings.services.email_service"))) { const char* email_service_names[EMAIL_PROVIDER_END]; + const char* email_service_images[EMAIL_PROVIDER_END]; for (u32 i = 0; i < EMAIL_PROVIDER_END; i++) { - email_service_names[i] = exporter::get_email_provider_implementation((email_provider)i).provider_name; + exporter::email_provider_impl impl = exporter::get_email_provider_implementation((email_provider)i); + email_service_names[i] = impl.provider_name; + email_service_images[i] = impl.icon_name; } - - if (ImGui::BeginCombo(locale::get("settings.services.email_service.provider"), email_service_names[new_ai_service.provider])) - { + + if (ImGui::BeginCombo("##emailProviderDropdown", NULL, 1 << 20)) { for (u32 n = 0; n < EMAIL_PROVIDER_END; n++) { + ImGui::PushID(n); bool is_selected = n == (uint32_t)new_email_service.provider; - if (ImGui::Selectable(email_service_names[n], is_selected)) { + if (ImGui::Selectable("##emailProviderOption", is_selected)) { new_email_service.provider = (email_provider)n; } + + ImGui::SameLine(); + ImGui::Image(assets::load_image(email_service_images[n]), ImVec2(16, 16)); + + ImGui::SameLine(); + ImGui::Text(email_service_names[n]); + + ImGui::PopID(); } ImGui::EndCombo(); } + + ImGui::BeginComboPreview(); + { + ImGui::Image(assets::load_image(email_service_images[new_email_service.provider]), ImVec2(16, 16)); + ImGui::Text(email_service_names[new_email_service.provider]); + + ImGui::EndComboPreview(); + } ImGui::InputTextWithHint(locale::get("settings.services.email_service.pubkey"), locale::get("settings.services.email_service.pubkey"), new_email_service.api_key, sizeof(new_email_service.api_key)); @@ -381,6 +420,7 @@ static void draw_email_service_ui() administration_writer::set_write_completed_event_callback(0); administration::set_email_service(new_email_service); } + ImGui::Spacing(); } } diff --git a/src/ui/ui_setup.cpp b/src/ui/ui_setup.cpp index 6140d5a..99d6d9f 100644 --- a/src/ui/ui_setup.cpp +++ b/src/ui/ui_setup.cpp @@ -14,53 +14,20 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <GLFW/glfw3.h> #include <imgui/imgui.h> #include <IconFontCppHeaders/IconsFontAwesome5.h> #include "ui.hpp" +#include "assets.hpp" #include "strops.hpp" #include "config.hpp" #include "locales.hpp" #include "administration_writer.hpp" #include "administration_reader.hpp" -#include <stb/stb_image.h> - -int ui::load_image(const char* filename) -{ - int width, height, channels; - unsigned char* data = stbi_load(filename, &width, &height, &channels, 0); - - if (!data) - { - return 0; - } - - GLuint textureID; - glGenTextures(1, &textureID); - glBindTexture(GL_TEXTURE_2D, textureID); - - // Set texture parameters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // Upload texture data - GLenum format = (channels == 4) ? GL_RGBA : GL_RGB; - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); - //glGenerateMipmap(GL_TEXTURE_2D); - - // Free image memory - stbi_image_free(data); - - return textureID; -} - void ui::draw_setup() { - static int img = load_image("/home/aldrik/Projects/open-books/build/splash.png"); + static int img = assets::load_image("img/splash"); ImVec2 area = ImGui::GetContentRegionAvail(); ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f)); |
