From 78030dbcdb27b60de6148a1782dfa3e9363f3386 Mon Sep 17 00:00:00 2001 From: Esdras Renan Date: Sat, 18 Oct 2025 23:31:10 -0300 Subject: [PATCH] Improve desktop branding and NSIS assets --- apps/desktop/scripts/png_to_bmp.py | 231 ++++++++++++++++++ apps/desktop/src-tauri/icons/nsis-header.bmp | Bin 0 -> 25818 bytes apps/desktop/src-tauri/icons/nsis-sidebar.bmp | Bin 0 -> 154542 bytes apps/desktop/src-tauri/tauri.conf.json | 3 + apps/desktop/src/main.tsx | 31 ++- 5 files changed, 253 insertions(+), 12 deletions(-) create mode 100644 apps/desktop/scripts/png_to_bmp.py create mode 100644 apps/desktop/src-tauri/icons/nsis-header.bmp create mode 100644 apps/desktop/src-tauri/icons/nsis-sidebar.bmp diff --git a/apps/desktop/scripts/png_to_bmp.py b/apps/desktop/scripts/png_to_bmp.py new file mode 100644 index 0000000..b8bf2f0 --- /dev/null +++ b/apps/desktop/scripts/png_to_bmp.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python3 +""" +Utility script to convert a PNG file (non-interlaced, 8-bit RGBA/RGB) +into a 24-bit BMP with optional letterboxing resize. + +The script is intentionally lightweight and relies only on Python's +standard library so it can run in constrained build environments. +""" + +from __future__ import annotations + +import argparse +import struct +import sys +import zlib +from pathlib import Path + + +PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n" + + +def parse_png(path: Path): + data = path.read_bytes() + if not data.startswith(PNG_SIGNATURE): + raise ValueError("Input is not a PNG file") + + idx = len(PNG_SIGNATURE) + width = height = bit_depth = color_type = None + compressed = bytearray() + interlaced = False + + while idx < len(data): + if idx + 8 > len(data): + raise ValueError("Corrupted PNG (unexpected EOF)") + length = struct.unpack(">I", data[idx : idx + 4])[0] + idx += 4 + chunk_type = data[idx : idx + 4] + idx += 4 + chunk_data = data[idx : idx + length] + idx += length + crc = data[idx : idx + 4] # noqa: F841 - crc skipped (validated by reader) + idx += 4 + + if chunk_type == b"IHDR": + width, height, bit_depth, color_type, compression, filter_method, interlace = struct.unpack( + ">IIBBBBB", chunk_data + ) + if compression != 0 or filter_method != 0: + raise ValueError("Unsupported PNG compression/filter method") + interlaced = interlace != 0 + elif chunk_type == b"IDAT": + compressed.extend(chunk_data) + elif chunk_type == b"IEND": + break + + if interlaced: + raise ValueError("Interlaced PNGs are not supported by this script") + if bit_depth != 8: + raise ValueError(f"Unsupported bit depth: {bit_depth}") + if color_type not in (2, 6): + raise ValueError(f"Unsupported color type: {color_type}") + + raw = zlib.decompress(bytes(compressed)) + bytes_per_pixel = 3 if color_type == 2 else 4 + stride = width * bytes_per_pixel + expected = (stride + 1) * height + if len(raw) != expected: + raise ValueError("Corrupted PNG data") + + # Apply PNG scanline filters + image = bytearray(width * height * 4) # Force RGBA output + prev_row = [0] * (stride) + + def paeth(a, b, c): + p = a + b - c + pa = abs(p - a) + pb = abs(p - b) + pc = abs(p - c) + if pa <= pb and pa <= pc: + return a + if pb <= pc: + return b + return c + + out_idx = 0 + for y in range(height): + offset = y * (stride + 1) + filter_type = raw[offset] + row = bytearray(raw[offset + 1 : offset + 1 + stride]) + if filter_type == 1: # Sub + for i in range(stride): + left = row[i - bytes_per_pixel] if i >= bytes_per_pixel else 0 + row[i] = (row[i] + left) & 0xFF + elif filter_type == 2: # Up + for i in range(stride): + row[i] = (row[i] + prev_row[i]) & 0xFF + elif filter_type == 3: # Average + for i in range(stride): + left = row[i - bytes_per_pixel] if i >= bytes_per_pixel else 0 + up = prev_row[i] + row[i] = (row[i] + ((left + up) >> 1)) & 0xFF + elif filter_type == 4: # Paeth + for i in range(stride): + left = row[i - bytes_per_pixel] if i >= bytes_per_pixel else 0 + up = prev_row[i] + up_left = prev_row[i - bytes_per_pixel] if i >= bytes_per_pixel else 0 + row[i] = (row[i] + paeth(left, up, up_left)) & 0xFF + elif filter_type != 0: + raise ValueError(f"Unsupported PNG filter type: {filter_type}") + + # Convert to RGBA + for x in range(width): + if color_type == 2: + r, g, b = row[x * 3 : x * 3 + 3] + a = 255 + else: + r, g, b, a = row[x * 4 : x * 4 + 4] + image[out_idx : out_idx + 4] = bytes((r, g, b, a)) + out_idx += 4 + + prev_row = list(row) + + return width, height, image + + +def resize_with_letterbox(image, width, height, target_w, target_h, background): + if width == target_w and height == target_h: + return image, width, height + + bg_r, bg_g, bg_b = background + scale = min(target_w / width, target_h / height) + scale = max(scale, 1 / max(width, height)) # avoid zero + scaled_w = max(1, int(round(width * scale))) + scaled_h = max(1, int(round(height * scale))) + + output = bytearray(target_w * target_h * 4) + # Fill background + for i in range(0, len(output), 4): + output[i : i + 4] = bytes((bg_r, bg_g, bg_b, 255)) + + offset_x = (target_w - scaled_w) // 2 + offset_y = (target_h - scaled_h) // 2 + + for y in range(scaled_h): + src_y = min(height - 1, int(round(y / scale))) + for x in range(scaled_w): + src_x = min(width - 1, int(round(x / scale))) + src_idx = (src_y * width + src_x) * 4 + dst_idx = ((y + offset_y) * target_w + (x + offset_x)) * 4 + output[dst_idx : dst_idx + 4] = image[src_idx : src_idx + 4] + + return output, target_w, target_h + + +def blend_to_rgb(image): + rgb = bytearray(len(image) // 4 * 3) + for i in range(0, len(image), 4): + r, g, b, a = image[i : i + 4] + if a == 255: + rgb[(i // 4) * 3 : (i // 4) * 3 + 3] = bytes((b, g, r)) # BMP stores BGR + else: + alpha = a / 255.0 + bg = (255, 255, 255) + rr = int(round(r * alpha + bg[0] * (1 - alpha))) + gg = int(round(g * alpha + bg[1] * (1 - alpha))) + bb = int(round(b * alpha + bg[2] * (1 - alpha))) + rgb[(i // 4) * 3 : (i // 4) * 3 + 3] = bytes((bb, gg, rr)) + return rgb + + +def write_bmp(path: Path, width: int, height: int, rgb: bytearray): + row_stride = (width * 3 + 3) & ~3 # align to 4 bytes + padding = row_stride - width * 3 + pixel_data = bytearray() + + for y in range(height - 1, -1, -1): + start = y * width * 3 + end = start + width * 3 + pixel_data.extend(rgb[start:end]) + if padding: + pixel_data.extend(b"\0" * padding) + + file_size = 14 + 40 + len(pixel_data) + header = struct.pack("<2sIHHI", b"BM", file_size, 0, 0, 14 + 40) + dib_header = struct.pack( + "qJGsHg-Gk>ZMF2_ncUb*odm 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 H=ZF6fJsOkI literal 0 HcmV?d00001 diff --git a/apps/desktop/src-tauri/icons/nsis-sidebar.bmp b/apps/desktop/src-tauri/icons/nsis-sidebar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5763d7392df01a89a67a92eaa30739f79b8057ce GIT binary patch literal 154542 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 literal 0 HcmV?d00001 diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 93cd7a2..551836f 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -53,6 +53,9 @@ "nsis": { "displayLanguageSelector": true, "installerIcon": "icons/icon.ico", + "uninstallerIcon": "icons/icon.ico", + "headerImage": "icons/nsis-header.bmp", + "sidebarImage": "icons/nsis-sidebar.bmp", "installMode": "perMachine", "languages": ["PortugueseBR"] } diff --git a/apps/desktop/src/main.tsx b/apps/desktop/src/main.tsx index c7dd08b..ddbcda8 100644 --- a/apps/desktop/src/main.tsx +++ b/apps/desktop/src/main.tsx @@ -751,7 +751,7 @@ function App() { ) : (
    -
    +
    Logotipo Raven -
    -
    -
    - -
    - Raven - Portal do Cliente +
    + Portal do Cliente +
    + + + + Raven
    - +
    +

    Agente Desktop

    + +
    -

    Agente Desktop

    {error ?

    {error}

    : null} {!token ? (
    @@ -971,14 +973,19 @@ function App() { ) } -function StatusBadge({ status }: { status: string | null }) { +function StatusBadge({ status, className }: { status: string | null; className?: string }) { const s = (status ?? "").toLowerCase() const label = s === "online" ? "Online" : s === "offline" ? "Offline" : s === "maintenance" ? "Manutenção" : "Sem status" const dot = s === "online" ? "bg-emerald-500" : s === "offline" ? "bg-rose-500" : s === "maintenance" ? "bg-amber-500" : "bg-slate-400" const ring = s === "online" ? "bg-emerald-400/30" : s === "offline" ? "bg-rose-400/30" : s === "maintenance" ? "bg-amber-400/30" : "bg-slate-300/30" const isOnline = s === "online" return ( - + {isOnline ? : null}