From 869933c858ae2bba20d3612fece966839ca52555 Mon Sep 17 00:00:00 2001 From: alvarsanmartin Date: Thu, 5 Mar 2026 12:01:06 +0100 Subject: [PATCH] Cambios esteticos y de ping para el servidor --- src/main/index.ts | 20 +- src/preload/index.d.ts | 12 +- src/preload/index.ts | 24 +- src/renderer/src/App.vue | 120 ++++--- src/renderer/src/assets/base.css | 100 +++--- src/renderer/src/components/ConfigView.vue | 357 ++++++++++++------- src/renderer/src/components/MainView.vue | 396 ++++++++++++++++----- src/services/NfcService.ts | 54 +-- 8 files changed, 740 insertions(+), 343 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 9711149..99b7997 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -4,7 +4,6 @@ import { electronApp, optimizer, is } from "@electron-toolkit/utils"; import icon from "../../resources/icon.png?asset"; import { NfcService } from "../services/NfcService"; -let nfcService: NfcService | null = null; function createWindow(): void { // Create the browser window. @@ -20,7 +19,11 @@ function createWindow(): void { }, }); - nfcService = new NfcService(); + new NfcService((event) => { + if (!mainWindow.isDestroyed()) { + mainWindow.webContents.send(`nfc:${event.type}`, event); + } + }); mainWindow.on("ready-to-show", () => { mainWindow.show(); @@ -57,6 +60,19 @@ app.whenReady().then(() => { // IPC test ipcMain.on("ping", () => console.log("pong")); + ipcMain.handle("ping:url", async (_event, url: string) => { + try { + const response = await fetch(url, { + method: "HEAD", + signal: AbortSignal.timeout(5000), + }); + return response.ok; + } catch (error) { + console.error(`Ping failed for ${url}:`, error); + return false; + } + }); + createWindow(); app.on("activate", function () { diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index ba88147..2bc397e 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -1,8 +1,18 @@ import { ElectronAPI } from "@electron-toolkit/preload"; +export interface NfcAPI { + onTag: (callback: (event: { uid: string }) => void) => void; + onRemoved: (callback: (event: { uid: string }) => void) => void; + onError: (callback: (event: { message: string }) => void) => void; + ping: (url: string) => Promise; + removeAllListeners: () => void; +} + declare global { interface Window { electron: ElectronAPI; - api: unknown; + api: { + nfc: NfcAPI; + }; } } diff --git a/src/preload/index.ts b/src/preload/index.ts index 7663d8d..a7409cb 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,8 +1,28 @@ -import { contextBridge } from "electron"; +import { contextBridge, ipcRenderer } from "electron"; import { electronAPI } from "@electron-toolkit/preload"; // Custom APIs for renderer -const api = {}; +const api = { + nfc: { + onTag: (callback: (event: { uid: string }) => void): void => { + ipcRenderer.on("nfc:tag", (_event, value) => callback(value)); + }, + onRemoved: (callback: (event: { uid: string }) => void): void => { + ipcRenderer.on("nfc:removed", (_event, value) => callback(value)); + }, + onError: (callback: (event: { message: string }) => void): void => { + ipcRenderer.on("nfc:error", (_event, value) => callback(value)); + }, + ping: (url: string): Promise => { + return ipcRenderer.invoke("ping:url", url); + }, + removeAllListeners: (): void => { + ipcRenderer.removeAllListeners("nfc:tag"); + ipcRenderer.removeAllListeners("nfc:removed"); + ipcRenderer.removeAllListeners("nfc:error"); + }, + }, +}; // Use `contextBridge` APIs to expose Electron APIs to // renderer only if context isolation is enabled, otherwise diff --git a/src/renderer/src/App.vue b/src/renderer/src/App.vue index b5fb8a0..105aaa1 100644 --- a/src/renderer/src/App.vue +++ b/src/renderer/src/App.vue @@ -13,15 +13,18 @@ const navigateTo = (view: "main" | "config"): void => { @@ -70,64 +58,82 @@ body { .app-container { display: flex; flex-direction: column; - min-height: 100vh; + height: 100vh; width: 100vw; + background-color: var(--te-bg); + color: var(--te-fg); } .app-header { display: flex; justify-content: space-between; align-items: center; - padding: 1rem 2rem; - background-color: #111111; - color: #ffffff; - border-bottom: 4px solid #111111; + padding: 12px 24px; + border-bottom: 1px solid var(--te-gray-light); } -.logo { - font-size: 1.25rem; - font-weight: 800; +.logo-section { + display: flex; + align-items: center; + gap: 12px; +} + +.logo-icon { + width: 12px; + height: 12px; + background-color: var(--te-orange); +} + +.logo-text { + font-family: var(--font-main); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.1em; text-transform: uppercase; } +.technical-info { + font-family: var(--font-mono); + font-size: 9px; + color: var(--te-gray); + text-transform: uppercase; +} + +.nav-section { + display: flex; +} + .nav-btn { - background: #ffffff; - border: 2px solid #ffffff; - color: #111111; - padding: 0.5rem 1rem; - font-size: 1rem; - font-weight: 800; + appearance: none; + border: 1px solid var(--te-fg); + background: transparent; + color: var(--te-fg); + padding: 4px 12px; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.05em; cursor: pointer; text-transform: uppercase; } +.nav-btn:hover { + background-color: var(--te-fg); + color: var(--te-bg); +} + .nav-btn:active { - background-color: #cccccc; - border-color: #cccccc; + transform: translateY(1px); } .app-content { flex: 1; + overflow: hidden; display: flex; - padding: 2rem; } @media (prefers-color-scheme: dark) { .app-header { - background-color: #ffffff; - color: #111111; - border-color: #ffffff; - } - - .nav-btn { - background-color: #111111; - border-color: #111111; - color: #ffffff; - } - - .nav-btn:active { - background-color: #333333; - border-color: #333333; + border-color: #222; } } diff --git a/src/renderer/src/assets/base.css b/src/renderer/src/assets/base.css index c8dbd6b..869acab 100644 --- a/src/renderer/src/assets/base.css +++ b/src/renderer/src/assets/base.css @@ -1,34 +1,35 @@ :root { - --ev-c-white: #ffffff; - --ev-c-white-soft: #f8f8f8; - --ev-c-white-mute: #f2f2f2; + /* Teenage Engineering Palette - LIGHT THEME DEFAULT */ + --te-bg: #f5f5f5; + --te-fg: #000000; + --te-orange: #ff5c00; + --te-gray: #888888; + --te-gray-light: #dadada; + --te-gray-dark: #333333; + --te-black: #000000; + --te-white: #ffffff; - --ev-c-black: #1b1b1f; - --ev-c-black-soft: #222222; - --ev-c-black-mute: #282828; + --color-background: var(--te-bg); + --color-text: var(--te-fg); - --ev-c-gray-1: #515c67; - --ev-c-gray-2: #414853; - --ev-c-gray-3: #32363f; + --font-main: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-mono: "JetBrains Mono", "Fira Code", monospace; - --ev-c-text-1: rgba(255, 255, 245, 0.86); - --ev-c-text-2: rgba(235, 235, 245, 0.6); - --ev-c-text-3: rgba(235, 235, 245, 0.38); - - --ev-button-alt-border: transparent; - --ev-button-alt-text: var(--ev-c-text-1); - --ev-button-alt-bg: var(--ev-c-gray-3); - --ev-button-alt-hover-border: transparent; - --ev-button-alt-hover-text: var(--ev-c-text-1); - --ev-button-alt-hover-bg: var(--ev-c-gray-2); + --spacing-unit: 4px; + --border-width: 1px; } -:root { - --color-background: var(--ev-c-black); - --color-background-soft: var(--ev-c-black-soft); - --color-background-mute: var(--ev-c-black-mute); +@media (prefers-color-scheme: dark) { + :root { + --te-bg: #121212; + --te-fg: #ffffff; + --te-gray: #666666; + --te-gray-light: #2a2a2a; + --te-gray-dark: #888888; - --color-text: var(--ev-c-text-1); + --color-background: var(--te-bg); + --color-text: var(--te-fg); + } } *, @@ -36,32 +37,45 @@ *::after { box-sizing: border-box; margin: 0; - font-weight: normal; -} - -ul { - list-style: none; + padding: 0; + transition: none !important; + /* TE style is instant */ } body { min-height: 100vh; color: var(--color-text); background: var(--color-background); - line-height: 1.6; - font-family: - Inter, - -apple-system, - BlinkMacSystemFont, - "Segoe UI", - Roboto, - Oxygen, - Ubuntu, - Cantarell, - "Fira Sans", - "Droid Sans", - "Helvetica Neue", - sans-serif; + font-family: var(--font-main); + font-size: 14px; + font-weight: 400; + line-height: 1.2; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + overflow: hidden; + /* Desktop app feel */ } + +h1, +h2, +h3, +h4 { + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +button { + font-family: var(--font-main); + font-weight: 600; + text-transform: uppercase; + cursor: pointer; + border: none; + border-radius: 0; + background: none; +} + +input { + font-family: var(--font-mono); +} \ No newline at end of file diff --git a/src/renderer/src/components/ConfigView.vue b/src/renderer/src/components/ConfigView.vue index eaf3348..563d22f 100644 --- a/src/renderer/src/components/ConfigView.vue +++ b/src/renderer/src/components/ConfigView.vue @@ -1,9 +1,18 @@ diff --git a/src/renderer/src/components/MainView.vue b/src/renderer/src/components/MainView.vue index 4ad2017..4f6906b 100644 --- a/src/renderer/src/components/MainView.vue +++ b/src/renderer/src/components/MainView.vue @@ -1,7 +1,40 @@ diff --git a/src/services/NfcService.ts b/src/services/NfcService.ts index da3eb3e..f0a18a8 100644 --- a/src/services/NfcService.ts +++ b/src/services/NfcService.ts @@ -1,39 +1,46 @@ -//import PCSC , { Reader } from '@tockawa/nfc-pcsc' -//import { NFC, Tag, Reader } from 'nfc-pcsc'; -import PCSC, { Card, Tag, Reader } from "@tockawa/nfc-pcsc"; -//import type Card from "@tockawa/nfc-pcsc" +import PCSC, { Tag, Reader } from "@tockawa/nfc-pcsc"; + +export type NfcEvent = + | { type: "tag"; uid: string } + | { type: "removed"; uid: string } + | { type: "error"; message: string }; export class NfcService { private nfc: PCSC; - constructor() { - this.nfc = new PCSC.default(); // Podrías pasar un logger aquí + private onEvent?: (event: NfcEvent) => void; + + constructor(onEvent?: (event: NfcEvent) => void) { + this.nfc = new PCSC.default(); + this.onEvent = onEvent; this.init(); } - private init() { + private init(): void { this.nfc.on("reader", (reader: Reader) => { console.log(`Lector detectado: ${reader.name}`); - //reader.autoProcessing = false; - //reader.aid = 'F222222222'; - - // Configuración del lector (opcional) - // reader.aid = 'F222222222'; reader.on("card", async (card: Tag) => { - // tag.uid es el identificador único del chip NFC - console.log(card); console.log(`Tarjeta detectada! UID: ${card.uid}`); - - // Aquí enviarías el UID al proceso de renderizado (tu UI) - // this.sendToWindow('nfc-tag-read', tag.uid); + if (this.onEvent) { + this.onEvent({ type: "tag", uid: card.uid }); + } }); reader.on("card.off", async (card: Tag) => { console.log(`Tarjeta retirada: ${card.uid}`); + if (this.onEvent) { + this.onEvent({ type: "removed", uid: card.uid }); + } }); - reader.on("error", (err: any) => { + reader.on("error", (err: Error) => { console.error(`Error en el lector ${reader.name}:`, err); + if (this.onEvent) { + this.onEvent({ + type: "error", + message: err.message || String(err), + }); + } }); reader.on("end", () => { @@ -41,13 +48,18 @@ export class NfcService { }); }); - this.nfc.on("error", (err: any) => { + this.nfc.on("error", (err: Error) => { console.error("Error general de NFC:", err); + if (this.onEvent) { + this.onEvent({ + type: "error", + message: err.message || String(err), + }); + } }); } - // Método para detener el servicio si es necesario - public stop() { + public stop(): void { // La mayoría de los lectores se cierran solos al cerrar la app } }