commit a8cc90704cc508bb6bea795bca26a9b3c4876fba Author: bryancito1090 Date: Sat Aug 29 00:01:32 2026 -0500 feat: scaffold front-end architecture and import thinkpad animation asset sequence diff --git a/.agents/skills/portfolio-backend/SKILL.md b/.agents/skills/portfolio-backend/SKILL.md new file mode 100644 index 0000000..611cb6b --- /dev/null +++ b/.agents/skills/portfolio-backend/SKILL.md @@ -0,0 +1,639 @@ +--- +name: portfolio-backend +description: Parámetros, reglas y estándares de arquitectura backend en C# / .NET 8/9 con Clean Architecture (Domain, Application, Infrastructure, WebApi), CQRS con MediatR, FluentValidation, EF Core MySQL, RBAC para administración y endpoints interactivos de prueba. +--- + +# Estándar de Arquitectura Backend — Clean Architecture en .NET (Portafolio Interactivo) + +Este documento define las reglas de arquitectura, flujo de datos por capas, convenciones de código, seguridad y persistencia con MySQL para el backend en **.NET 8/9** del portafolio interactivo. + +--- + +## 1. Mentalidad de Arquitecto Backend Senior — Reglas de Oro + +El agente actúa como un **Ingeniero Principal y Arquitecto Backend Senior con 10+ años de experiencia** en desarrollo con .NET y arquitecturas limpias de alto rendimiento. + +1. **Clean Architecture & Regla de Dependencia**: + - Las dependencias van estrictamente hacia adentro: + `WebApi` ──► `Application` ──► `Domain` ◄── `Infrastructure` + - El núcleo (`Domain`) nunca depende de EF Core, MySQL, MediatR, ASP.NET Core ni librerías externas. + - La inversión de dependencias permite aislar la lógica de negocio: `Domain` declara los contratos (`IProjectRepository`), e `Infrastructure` los implementa. +2. **Filosofía Ponytail (Prevención de Sobreingeniería)**: + - **Prohibido Generic Repository genérico** (`IRepository` con 30 métodos que nadie usa). Diseñar interfaces de repositorio específicas y quirúrgicas para cada raíz de agregado (Aggregate Root). + - **Prohibido envolver DbContext en un UnitOfWork casero adicional**: `DbContext` de EF Core ya es un *Unit of Work* y *Identity Map* probado en producción. + - **Prohibido AutoMapper**: En su lugar, proyectar directamente con `.Select()` de LINQ para lecturas, o constructores/métodos estáticos en records para mapeos explícitos, seguros y sin overhead de reflection. + - **Records posicionales**: Usar `public sealed record ...` para DTOs, Commands y Queries. Cero clases verbosas con getters/setters tradicionales. + - **Primary Constructors**: Aprovechar la sintaxis moderna de C# 12+ para inyección de dependencias limpia en una sola línea. +3. **Cero operaciones síncronas en I/O**: + - Todo acceso a base de datos, hasher o servicios de token es estrictamente asíncrono (`async/await`) recibiendo siempre `CancellationToken`. +4. **Manejo Centralizado de Excepciones**: + - Prohibido capturar excepciones dentro de los controladores. Se utiliza `AddProblemDetails()` y `IExceptionHandler` según el estándar **RFC 7807**. + +--- + +## 2. Límites de Líneas por Archivo y Modularidad Backend + +Para mantener un balance óptimo entre cohesión y separación de responsabilidades (evitando tanto las *God Classes* monolíticas como la sobre-fragmentación artificial en decenas de micro-archivos), se establecen los siguientes umbrales tolerables: + +| Tipo de Archivo | Capa / Ubicación | Líneas Ideales | Máximo Tolerable | Acción al Exceder el Límite | +|---|---|---|---|---| +| **Controlador REST** | `WebApi/Controllers/` | **50 – 120** | **200** | Prohibido incluir lógica de negocio. Solo validar HTTP, delegar a MediatR (`ISender`) y retornar `ActionResult`. Permite agrupar las rutas CRUD completas de un recurso. | +| **Command / Query Handler** | `Application/Features/.../` | **40 – 90** | **160** | Principio *One Handler per File*. Si supera 160 líneas, encapsular reglas dentro de la Entidad de Dominio o delegar a subservicios especializados. | +| **Entidad de Dominio** | `Domain/Entities/` | **60 – 130** | **220** | Contiene propiedades, reglas de negocio y fábrica estática `Create()`. Extraer sub-conceptos a Value Objects si crece. | +| **DTO / Command / Query** | `Application/.../DTOs/` | **10 – 40** | **80** | Usar `public sealed record` posicionales. Si se agrupan varios DTOs relacionados en un archivo (ej: `ProjectDtos.cs`), el archivo puede alcanzar hasta **120 líneas**. | +| **Validador FluentValidation**| `Application/.../Validators/` | **25 – 60** | **100** | Encapsular validaciones complejas o reutilizables en métodos de extensión propios. | +| **Repositorio de Infraestructura** | `Infrastructure/Persistence/` | **50 – 100** | **180** | Implementar los métodos de consulta y persistencia requeridos por el contrato de dominio (`IProjectRepository`). | +| **Configuración EF Core (Fluent API)** | `Infrastructure/Configurations/` | **30 – 70** | **120** | Una clase por entidad (`IEntityTypeConfiguration`). | +| **DbContext** | `Infrastructure/Context/` | **40 – 90** | **150** | Registrar los `DbSet` y llamar a `modelBuilder.ApplyConfigurationsFromAssembly(...)`. | + +### Reglas de Descomposición Modular: +- **One Handler per File**: Cada caso de uso (Command o Query) y su Handler viven en su propio archivo independiente para facilitar pruebas unitarias y navegación. +- **División de Controladores**: Un controlador puede manejar cómodamente las operaciones CRUD de una entidad (hasta 150-200 líneas). Solo se divide si mezcla recursos no relacionados o responsabilidades administrativas distintas. + +--- + +## 3. Mapa de Capas y Responsabilidades + +``` +backend/src/ +├── Portfolio.Domain/ <- Núcleo del Negocio. Cero dependencias externas. +│ ├── Entities/ <- Project, Skill, User, Role, InteractiveLog +│ ├── Enums/ <- ProjectCategory, SkillLevel, UserRole +│ ├── Exceptions/ <- DomainException, NotFoundException +│ └── Interfaces/ <- IProjectRepository, IUserRepository, IUnitOfWork (opcional) +│ +├── Portfolio.Application/ <- Casos de Uso (CQRS). Solo depende de Domain. +│ ├── Common/ +│ │ ├── Behaviors/ <- ValidationBehavior (MediatR Pipeline) +│ │ └── Interfaces/ <- IJwtTokenGenerator, IPasswordHasher +│ ├── Features/ <- Organización vertical por Feature (Vertical Slice inside App) +│ │ ├── Projects/ +│ │ │ ├── Commands/ <- CreateProjectCommand, UpdateProjectCommand +│ │ │ ├── Queries/ <- GetProjectsQuery, GetProjectByIdQuery +│ │ │ ├── DTOs/ <- ProjectResponseDto, CreateProjectRequestDto +│ │ │ └── Validators/ <- CreateProjectCommandValidator +│ │ ├── Interactive/ <- Endpoints de prueba para visitantes +│ │ │ ├── Queries/ <- GetServerTelemetryQuery, PingServerQuery +│ │ │ └── DTOs/ <- ServerTelemetryDto, PingResponseDto +│ │ └── Auth/ <- Login, RefreshToken, RBAC +│ │ ├── Commands/ <- LoginCommand, RefreshTokenCommand +│ │ ├── DTOs/ <- AuthResponseDto, LoginRequestDto +│ │ └── Validators/ <- LoginCommandValidator +│ └── DependencyInjection.cs +│ +├── Portfolio.Infrastructure/ <- Implementación Técnica. Depende de Application y Domain. +│ ├── Persistence/ +│ │ ├── Context/ <- PortfolioDbContext (EF Core con MySQL) +│ │ ├── Configurations/ <- EntityTypeConfigurations (Fluent API) +│ │ └── Repositories/ <- ProjectRepository, UserRepository +│ ├── Security/ +│ │ ├── JwtTokenGenerator.cs <- Generación y validación de tokens JWT +│ │ └── PasswordHasher.cs <- Hasheo de contraseñas (BCrypt / PBKDF2) +│ └── DependencyInjection.cs +│ +└── Portfolio.WebApi/ <- Punto de Entrada HTTP. Depende de Application e Infrastructure. + ├── Controllers/ <- ProjectsController, InteractiveController, AuthController + ├── Middleware/ <- GlobalExceptionHandler (ProblemDetails RFC 7807) + ├── Security/ <- HasPermissionAttribute o Authorize policies + ├── Program.cs <- Pipeline HTTP, DI y configuración + └── appsettings.json +``` + +--- + +## 4. Flujo de Datos Obligatorio para Endpoints (Data Flow Pipeline) + +Para mantener total coherencia y evitar sobreingeniería, todo endpoint debe seguir estrictamente este flujo de 7 pasos secuenciales: + +``` +[1. Request HTTP / DTO] + │ + ▼ +[2. Command / Query (MediatR IRequest)] + │ + ▼ +[3. FluentValidation (Pipeline Behavior)] + │ + ▼ +[4. Handler (IRequestHandler)] + │ + ▼ +[5. Dominio & Persistencia] + ├── Si es ESCRITURA: Entidad de Dominio ejecuta regla ──► IRepository.AddAsync() ──► SaveChangesAsync() + └── Si es LECTURA: DbContext.Set().AsNoTracking().Select(...) ──► Directo a DTO (Sin overhead) + │ + ▼ +[6. Response DTO (Record Inmutable)] + │ + ▼ +[7. Controller Action] (Retorna ActionResult con 200, 201 o 204) +``` + +--- + +## 5. Implementación Paso a Paso con Código Real + +### Paso 1: Definir los DTOs (Records Inmutables) +Los DTOs definen los contratos externos sin exponer la estructura de base de datos. + +```csharp +// Application/Features/Projects/DTOs/ProjectDtos.cs +namespace Portfolio.Application.Features.Projects.DTOs; + +public sealed record CreateProjectRequestDto( + string Title, + string Description, + string RepositoryUrl, + string? LiveDemoUrl, + string[] Technologies, + bool IsFeatured +); + +public sealed record ProjectResponseDto( + Guid Id, + string Title, + string Description, + string RepositoryUrl, + string? LiveDemoUrl, + IReadOnlyList Technologies, + bool IsFeatured, + DateTime CreatedAtUtc +); +``` + +--- + +### Paso 2: Command o Query (MediatR `IRequest`) +Representa la intención del usuario. El Command muta estado; la Query solo consulta. + +```csharp +// Application/Features/Projects/Commands/CreateProjectCommand.cs +using MediatR; +using Portfolio.Application.Features.Projects.DTOs; + +namespace Portfolio.Application.Features.Projects.Commands; + +public sealed record CreateProjectCommand( + string Title, + string Description, + string RepositoryUrl, + string? LiveDemoUrl, + string[] Technologies, + bool IsFeatured +) : IRequest; +``` + +--- + +### Paso 3: Validación con FluentValidation +Valida la integridad de los datos antes de que el Handler sea ejecutado. + +```csharp +// Application/Features/Projects/Validators/CreateProjectCommandValidator.cs +using FluentValidation; +using Portfolio.Application.Features.Projects.Commands; + +namespace Portfolio.Application.Features.Projects.Validators; + +public sealed class CreateProjectCommandValidator : AbstractValidator +{ + public CreateProjectCommandValidator() + { + RuleFor(x => x.Title) + .NotEmpty().WithMessage("El título del proyecto es obligatorio.") + .MaximumLength(150).WithMessage("El título no puede exceder 150 caracteres."); + + RuleFor(x => x.Description) + .NotEmpty().WithMessage("La descripción es obligatoria.") + .MaximumLength(1000).WithMessage("La descripción no puede exceder 1000 caracteres."); + + RuleFor(x => x.RepositoryUrl) + .NotEmpty().WithMessage("La URL del repositorio es obligatoria.") + .Must(uri => Uri.TryCreate(uri, UriKind.Absolute, out _)) + .WithMessage("La URL del repositorio no es válida."); + + RuleFor(x => x.Technologies) + .NotEmpty().WithMessage("Debe incluir al menos una tecnología."); + } +} +``` + +#### Pipeline de Validación Automático (ValidationBehavior) +MediatR ejecuta este pipeline antes del Handler; si la validación falla, lanza `ValidationException` que el `GlobalExceptionHandler` captura retornando HTTP 422: + +```csharp +// Application/Common/Behaviors/ValidationBehavior.cs +using FluentValidation; +using MediatR; + +namespace Portfolio.Application.Common.Behaviors; + +public sealed class ValidationBehavior(IEnumerable> validators) + : IPipelineBehavior + where TRequest : IRequest +{ + public async Task Handle( + TRequest request, + RequestHandlerDelegate next, + CancellationToken cancellationToken) + { + if (!validators.Any()) return await next(); + + var context = new ValidationContext(request); + var validationResults = await Task.WhenAll( + validators.Select(v => v.ValidateAsync(context, cancellationToken))); + + var failures = validationResults + .SelectMany(r => r.Errors) + .Where(f => f != null) + .ToList(); + + if (failures.Count != 0) + throw new ValidationException(failures); + + return await next(); + } +} +``` + +--- + +### Paso 4: Handler (`IRequestHandler`) +Orquesta la lógica de negocio delegando la persistencia a través de la interfaz del repositorio. + +```csharp +// Application/Features/Projects/Commands/CreateProjectCommandHandler.cs +using MediatR; +using Portfolio.Application.Features.Projects.DTOs; +using Portfolio.Domain.Entities; +using Portfolio.Domain.Interfaces; + +namespace Portfolio.Application.Features.Projects.Commands; + +public sealed class CreateProjectCommandHandler(IProjectRepository projectRepository) + : IRequestHandler +{ + public async Task Handle( + CreateProjectCommand request, + CancellationToken cancellationToken) + { + // Creación y encapsulación en entidad de dominio + var project = Project.Create( + request.Title, + request.Description, + request.RepositoryUrl, + request.LiveDemoUrl, + request.Technologies, + request.IsFeatured + ); + + await projectRepository.AddAsync(project, cancellationToken); + await projectRepository.SaveChangesAsync(cancellationToken); + + return new ProjectResponseDto( + project.Id, + project.Title, + project.Description, + project.RepositoryUrl, + project.LiveDemoUrl, + project.Technologies, + project.IsFeatured, + project.CreatedAtUtc + ); + } +} +``` + +--- + +### Paso 5: Dominio e Inversión de Persistencia + +#### A. Entidad de Dominio Pura (Sin EF Core) +```csharp +// Domain/Entities/Project.cs +namespace Portfolio.Domain.Entities; + +public sealed class Project +{ + public Guid Id { get; private set; } + public string Title { get; private set; } = string.Empty; + public string Description { get; private set; } = string.Empty; + public string RepositoryUrl { get; private set; } = string.Empty; + public string? LiveDemoUrl { get; private set; } + public List Technologies { get; private set; } = []; + public bool IsFeatured { get; private set; } + public DateTime CreatedAtUtc { get; private set; } + + private Project() { } // Constructor privado para EF Core + + public static Project Create( + string title, + string description, + string repoUrl, + string? demoUrl, + string[] technologies, + bool isFeatured) + { + return new Project + { + Id = Guid.NewGuid(), + Title = title.Trim(), + Description = description.Trim(), + RepositoryUrl = repoUrl.Trim(), + LiveDemoUrl = demoUrl?.Trim(), + Technologies = [..technologies], + IsFeatured = isFeatured, + CreatedAtUtc = DateTime.UtcNow + }; + } +} +``` + +#### B. Contrato en Dominio (Puerto) +```csharp +// Domain/Interfaces/IProjectRepository.cs +using Portfolio.Domain.Entities; + +namespace Portfolio.Domain.Interfaces; + +public interface IProjectRepository +{ + Task GetByIdAsync(Guid id, CancellationToken cancellationToken); + Task> GetAllAsync(CancellationToken cancellationToken); + Task AddAsync(Project project, CancellationToken cancellationToken); + void Remove(Project project); + Task SaveChangesAsync(CancellationToken cancellationToken); +} +``` + +#### C. Implementación en Infraestructura (Adaptador EF Core MySQL) +```csharp +// Infrastructure/Persistence/Repositories/ProjectRepository.cs +using Microsoft.EntityFrameworkCore; +using Portfolio.Domain.Entities; +using Portfolio.Domain.Interfaces; +using Portfolio.Infrastructure.Persistence.Context; + +namespace Portfolio.Infrastructure.Persistence.Repositories; + +public sealed class ProjectRepository(PortfolioDbContext context) : IProjectRepository +{ + public async Task GetByIdAsync(Guid id, CancellationToken cancellationToken) + => await context.Projects.FirstOrDefaultAsync(p => p.Id == id, cancellationToken); + + public async Task> GetAllAsync(CancellationToken cancellationToken) + => await context.Projects.AsNoTracking().ToListAsync(cancellationToken); + + public async Task AddAsync(Project project, CancellationToken cancellationToken) + => await context.Projects.AddAsync(project, cancellationToken); + + public void Remove(Project project) + => context.Projects.Remove(project); + + public async Task SaveChangesAsync(CancellationToken cancellationToken) + => await context.SaveChangesAsync(cancellationToken); +} +``` + +#### D. Contexto EF Core MySQL y Migraciones + +```csharp +// Infrastructure/Persistence/Context/PortfolioDbContext.cs +using Microsoft.EntityFrameworkCore; +using Portfolio.Domain.Entities; + +namespace Portfolio.Infrastructure.Persistence.Context; + +public sealed class PortfolioDbContext(DbContextOptions options) : DbContext(options) +{ + public DbSet Projects => Set(); + public DbSet Skills => Set(); + public DbSet Users => Set(); + public DbSet RefreshTokens => Set(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfigurationsFromAssembly(typeof(PortfolioDbContext).Assembly); + base.OnModelCreating(modelBuilder); + } +} +``` + +Registro de Inyección en `Infrastructure/DependencyInjection.cs`: +```csharp +public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) +{ + var connectionString = configuration.GetConnectionString("DefaultConnection"); + services.AddDbContext(options => + options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))); + + services.AddScoped(); + return services; +} +``` + +Comandos de Migración MySQL: +```bash +dotnet ef migrations add InitialCreate --project src/Portfolio.Infrastructure --startup-project src/Portfolio.WebApi +dotnet ef database update --project src/Portfolio.Infrastructure --startup-project src/Portfolio.WebApi +``` + +--- + +### Paso 6 & 7: Controlador REST en WebApi + +```csharp +// WebApi/Controllers/ProjectsController.cs +using MediatR; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Portfolio.Application.Features.Projects.Commands; +using Portfolio.Application.Features.Projects.DTOs; +using Portfolio.Application.Features.Projects.Queries; + +namespace Portfolio.WebApi.Controllers; + +[ApiController] +[Route("api/v1/[controller]")] +public sealed class ProjectsController(ISender sender) : ControllerBase +{ + [HttpGet] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task>> GetAll( + CancellationToken cancellationToken) + { + var result = await sender.Send(new GetProjectsQuery(), cancellationToken); + return Ok(result); + } + + [HttpPost] + [Authorize(Roles = "Admin")] // Protegido por RBAC para que Bryan gestione sus proyectos + [ProducesResponseType(typeof(ProjectResponseDto), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] + public async Task> Create( + [FromBody] CreateProjectRequestDto request, + CancellationToken cancellationToken) + { + var command = new CreateProjectCommand( + request.Title, + request.Description, + request.RepositoryUrl, + request.LiveDemoUrl, + request.Technologies, + request.IsFeatured + ); + + var created = await sender.Send(command, cancellationToken); + return CreatedAtAction(nameof(GetAll), new { id = created.Id }, created); + } +} +``` + +--- + +## 6. Endpoints Interactivos Públicos (Live Playground) + +Para que los visitantes y reclutadores interactúen con el backend sin autenticación pero con rate-limiting nativo para evitar abusos o denegación de servicio: + +```csharp +// WebApi/Controllers/InteractiveController.cs +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.RateLimiting; +using Portfolio.Application.Features.Interactive.DTOs; +using Portfolio.Application.Features.Interactive.Queries; + +namespace Portfolio.WebApi.Controllers; + +[ApiController] +[Route("api/v1/[controller]")] +[EnableRateLimiting("interactive-policy")] // Límite nativo de .NET 8/9 (ej. 30 req/min por IP) +public sealed class InteractiveController(ISender sender) : ControllerBase +{ + [HttpGet("ping")] + [ProducesResponseType(typeof(PingResponseDto), StatusCodes.Status200OK)] + public async Task> Ping(CancellationToken cancellationToken) + { + var response = await sender.Send(new PingServerQuery(), cancellationToken); + return Ok(response); + } + + [HttpGet("telemetry")] + [ProducesResponseType(typeof(ServerTelemetryDto), StatusCodes.Status200OK)] + public async Task> Telemetry(CancellationToken cancellationToken) + { + var response = await sender.Send(new GetServerTelemetryQuery(), cancellationToken); + return Ok(response); + } +} +``` + +Configuración en `WebApi/Program.cs`: +```csharp +builder.Services.AddRateLimiter(options => +{ + options.RejectionStatusCode = StatusCodes.Status429TooManyRequests; + options.AddFixedWindowLimiter("interactive-policy", opt => + { + opt.PermitLimit = 30; // Máximo 30 peticiones + opt.Window = TimeSpan.FromMinutes(1); // por cada minuto + opt.QueueLimit = 0; + }); +}); +// En el pipeline: +app.UseRateLimiter(); +``` + +--- + +## 7. Seguridad y RBAC (Administración para Bryan) + +1. **Autenticación con JWT**: + - Tokens con tiempo de expiración corto (15 a 30 minutos). + - `ClockSkew = TimeSpan.Zero` obligatorio para no permitir ventanas de tokens vencidos. +2. **Refresh Token Rotation en MySQL**: + - Cada refresh token se almacena en la tabla `auth_refresh_tokens` con estado `IsRevoked`, `ExpiresAtUtc` y `ReplacedByToken`. + - Al renovar, el token anterior se revoca inmediatamente y se emite uno nuevo. Si un token revocado intenta usarse, se revoca toda la cadena por sospecha de compromiso. + +```csharp +// Domain/Entities/RefreshToken.cs +namespace Portfolio.Domain.Entities; + +public sealed class RefreshToken +{ + public Guid Id { get; private set; } + public Guid UserId { get; private set; } + public string TokenHash { get; private set; } = string.Empty; + public DateTime ExpiresAtUtc { get; private set; } + public bool IsRevoked { get; private set; } + public DateTime CreatedAtUtc { get; private set; } + + public bool IsActive => !IsRevoked && DateTime.UtcNow < ExpiresAtUtc; + public void Revoke() => IsRevoked = true; +} +``` + +3. **RBAC Simple y Efectivo**: + - Roles en Claim: `Role: Admin`, `Role: Visitor`. + - Bryan inicia sesión desde el portal o terminal (`POST /api/v1/auth/login`) y recibe los tokens para realizar operaciones de escritura (`POST`, `PUT`, `DELETE`). + - Los visitantes solo tienen acceso de lectura a los proyectos/skills y a los endpoints públicos de `/api/v1/interactive/*`. + +--- + +## 8. Manejo Centralizado de Errores — ProblemDetails RFC 7807 + +```csharp +// WebApi/Middleware/GlobalExceptionHandler.cs +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.AspNetCore.Mvc; +using Portfolio.Domain.Exceptions; + +namespace Portfolio.WebApi.Middleware; + +public sealed class GlobalExceptionHandler(ILogger logger) : IExceptionHandler +{ + public async ValueTask TryHandleAsync( + HttpContext httpContext, + Exception exception, + CancellationToken cancellationToken) + { + logger.LogError(exception, "Excepción capturada: {Message}", exception.Message); + + var (statusCode, title) = exception switch + { + NotFoundException => (StatusCodes.Status404NotFound, "Recurso no encontrado"), + FluentValidation.ValidationException => (StatusCodes.Status422UnprocessableEntity, "Error de validación"), + UnauthorizedAccessException => (StatusCodes.Status401Unauthorized, "No autorizado"), + _ => (StatusCodes.Status500InternalServerError, "Error interno del servidor") + }; + + var problemDetails = new ProblemDetails + { + Status = statusCode, + Title = title, + Detail = exception.Message, + Instance = httpContext.Request.Path + }; + + httpContext.Response.StatusCode = statusCode; + await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken); + return true; + } +} +``` + +--- + +## 9. Anti-Patrones Prohibidos en Backend + +| Anti-patrón Prohibido | Solución Obligatoria | +|---|---| +| Crear `GenericRepository` con docenas de métodos genéricos | Contratos de repositorio específicos por entidad con los métodos exactos necesarios | +| Envolver `DbContext` en una clase `UnitOfWork` casera | Usar `PortfolioDbContext` directamente como Unit of Work | +| Usar AutoMapper con perfiles complejos y mapeo por reflexión | Proyección directa `.Select()` en Queries o mapeo estático con Records | +| Clases DTO tradicionales con getters/setters y constructores boilerplate | `public sealed record ...` inmutable de C# moderno | +| Inyección de dependencias clásica con campos privados repetidos | **Primary Constructors** de C# 12+ | +| Rutas en camelCase o PascalCase en controladores | kebab-case con versionado explícito: `[Route("api/v1/[controller]")]` | +| `SaveChanges()` síncrono en cualquier punto | Siempre `SaveChangesAsync(cancellationToken)` | +| Catch de excepciones dentro de acciones del controlador | Delegar al `IExceptionHandler` global con RFC 7807 | +| Consultas de lectura que cargan entidades con tracking | Usar `.AsNoTracking()` o `.Select()` directo a DTO | diff --git a/.agents/skills/portfolio-frontend/SKILL.md b/.agents/skills/portfolio-frontend/SKILL.md new file mode 100644 index 0000000..f26290c --- /dev/null +++ b/.agents/skills/portfolio-frontend/SKILL.md @@ -0,0 +1,665 @@ +--- +name: portfolio-frontend +description: Parámetros, reglas y estándares de arquitectura frontend con Clean Architecture en Angular (Signals reactivos, Domain Ports & Adapters, Application Stores, Presentation OnPush, Terminal interactiva, Retro OS Canvas y 3D Scroll Hero). +--- + +# Estándar de Desarrollo Frontend — Clean Architecture en Angular (Portafolio Interactivo) + +Este documento establece las directrices de arquitectura, organización de código, reactividad con **Angular Signals**, desacoplamiento hexagonal (**Ports & Adapters**) e integración de módulos interactivos (Terminal, Retro OS, Videojuegos Canvas y Hero 3D) para el cliente web del portafolio. + +--- + +## 1. Mentalidad de Arquitecto Frontend Senior — Reglas de Oro + +El agente actúa como un **Arquitecto Frontend Senior con 10+ años de experiencia** en aplicaciones enterprise y experiencias web interactivas de alto rendimiento. + +1. **Clean Architecture / Inversión de Dependencias (DIP)**: + - Las capas internas nunca conocen a las externas. El código fuente solo apunta hacia el centro: + `Presentation` ──► `Application` ──► `Domain` ◄── `Infrastructure / Data` + - El **Dominio** es TypeScript puro, agnóstico del framework (cero imports de `@angular/*`, cero `HttpClient`). + - Los cambios de endpoints en el backend solo afectan a `infrastructure/`, jamás a la UI ni al Dominio. +2. **Reactividad Primitiva con Signals & OnPush**: + - Todo componente en la capa de presentación debe usar `changeDetection: ChangeDetectionStrategy.OnPush`. + - El estado se administra exclusivamente con **Angular Signals** (`signal`, `computed`, `effect`). + - Prohibido abusar de `BehaviorSubject` para estados locales o UI cuando `signal()` es nativo, más limpio y de menor overhead. + - RxJS se reserva exclusivamente para flujos asíncronos y cancelables en adaptadores de infraestructura (`HttpClient`). +3. **Smart vs. Dumb Components**: + - **Smart Components (Pages/Containers)**: Inyectan Application Stores o Facades, coordinan la vista y pasan señales a los componentes hijos. + - **Dumb / Presentational Components (UI)**: Reciben datos estrictamente por `input()` / `input.required()` y notifican eventos mediante `output()`. Cero inyecciones de repositorios o servicios HTTP. +4. **Sintaxis Moderna de Control de Flujo**: + - Uso obligatorio de la nueva sintaxis `@if`, `@for` (con `track`), `@switch` y `@defer`. + - Prohibido el uso de directivas estructurales legadas (`*ngIf`, `*ngFor`). +5. **Filosofía Ponytail (Cero Sobreingeniería)**: + - Usar las herramientas nativas de Angular y la Web Platform antes de añadir paquetes `npm`. + - Mini-juego retro: Usar la **API nativa Canvas 2D** en lugar de motores de juego pesados de 500KB. + - Scroll 3D: Implementar con Three.js optimizado o Canvas/CSS 3D ligero, pausando el render loop cuando no esté visible con `IntersectionObserver`. + +--- + +## 2. Límites de Líneas por Archivo y Modularidad de Componentes + +Para equilibrar la modularidad sin caer en una fragmentación excesiva (evitando crear cientos de micro-archivos innecesarios), se establecen los siguientes umbrales tolerables: + +| Tipo de Archivo | Extensión / Ubicación | Líneas Ideales | Máximo Tolerable | Acción al Exceder el Límite | +|---|---|---|---|---| +| **Componente TypeScript** | `*.component.ts` | **80 – 180** | **280** | Extraer lógica pesada al Store o delegar bloques de vista en subcomponentes Dumb. | +| **Plantilla HTML** | `*.component.html` | **50 – 130** | **200** | Descomponer bloques visuales complejos en subcomponentes presentacionales hijos. | +| **Hojas de Estilo** | `*.component.css` | **40 – 100** | **160** | Consumir tokens globales de diseño y utilidades para no duplicar reglas CSS. | +| **Application Stores** | `*.store.ts` | **80 – 160** | **240** | Dividir por dominio funcional o sub-estado específico si acumula demasiados flujos. | +| **Servicios / Casos de Uso** | `*.service.ts` / `*.usecase.ts` | **50 – 120** | **180** | Aplicar Single Responsibility (SRP): mantener el servicio acotado a su función. | +| **Modelos y Puertos** | `domain/models/`, `ports/` | **30 – 70** | **120** | Separar modelos o agrupar contratos relacionados de forma coherente. | +| **Adaptadores y Repositorios**| `infrastructure/adapters/` | **60 – 120** | **180** | Mantener solo las llamadas HTTP del recurso específico. | +| **Mappers y DTOs** | `infrastructure/mappers/` | **30 – 80** | **130** | Dividir funciones de mapeo si el DTO tiene muchas transformaciones complejas. | +| **Motor de Juego / Canvas** | `retro-game/` | **100 – 200** | **320** | Separar en módulos si el juego crece: `game-engine.ts`, `renderer.ts`, `input-handler.ts`. | + +### Criterio de Descomposición Inteligente: +- **No forzar la micro-fragmentación**: No es necesario crear un subcomponente por cada botón o etiqueta. Se crea un nuevo subcomponente cuando una sección visual o interactiva tiene identidad propia (ej. `terminal-prompt`, `retro-window-header`, `game-scoreboard`). +- **Control Flow Anidado**: Si una plantilla HTML supera **3 a 4 niveles de anidamiento** de bloques `@if` o `@for`, es momento de extraer ese bloque interno a un subcomponente Dumb dedicado para mantener la legibilidad. + +--- + +## 3. Diagrama de Capas de Arquitectura Limpia + +``` +┌─────────────────────────────────────────────────────────────┐ +│ PRESENTATION LAYER (Angular) │ +│ - Pages (Hero, Terminal, Retro OS, Projects, Admin) │ +│ - Dumb Components (WindowFrame, TerminalView, GameCanvas) │ +│ - UI Directives & Pipes │ +└──────────────────────────────┬──────────────────────────────┘ + │ consume +┌──────────────────────────────▼──────────────────────────────┐ +│ APPLICATION LAYER (Stores & Use Cases) │ +│ - ProjectStore (Signals) - TerminalEngine (Signals) │ +│ - RetroOsStore (Signals) - AuthStore (RBAC Signals) │ +│ - InteractiveApiStore │ +└──────────────────────────────┬──────────────────────────────┘ + │ orquesta usando puertos +┌──────────────────────────────▼──────────────────────────────┐ +│ DOMAIN LAYER (Core Puro) │ +│ - Models (Project, Skill, Command, RetroWindow, AuthUser) │ +│ - Ports (Abstract Classes / Injection Tokens) │ +│ - Domain Value Objects & Business Rules │ +└──────────────────────────────▲──────────────────────────────┘ + │ implementa contratos de +┌──────────────────────────────┴──────────────────────────────┐ +│ INFRASTRUCTURE / DATA LAYER (Adapters) │ +│ - HttpProjectRepository - HttpInteractiveRepository │ +│ - HttpAuthRepository - LocalStorageAdapter │ +│ - DTOs & Mappers puros (DTO <-> Model) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 4. Estructura de Directorios del Proyecto Frontend + +``` +frontend/src/app/ +├── domain/ <- TypeScript PURO (Sin Angular imports) +│ ├── models/ <- Modelos inmutables de negocio +│ │ ├── project.model.ts +│ │ ├── skill.model.ts +│ │ ├── terminal-command.model.ts +│ │ ├── retro-os.model.ts +│ │ ├── interactive-demo.model.ts +│ │ └── auth.model.ts +│ └── ports/ <- Contratos abstractos (Interfaces / Abstract Classes) +│ ├── project.port.ts +│ ├── interactive-demo.port.ts +│ └── auth.port.ts +│ +├── infrastructure/ <- Implementaciones técnicas y adaptadores +│ ├── dtos/ <- Contratos directos de la API .NET +│ │ ├── project.dto.ts +│ │ ├── interactive-demo.dto.ts +│ │ └── auth.dto.ts +│ ├── mappers/ <- Transformadores puros (DTO <-> Model) +│ │ ├── project.mapper.ts +│ │ ├── interactive-demo.mapper.ts +│ │ └── auth.mapper.ts +│ └── adapters/ <- Implementación HTTP con HttpClient +│ ├── http-project.adapter.ts +│ ├── http-interactive-demo.adapter.ts +│ └── http-auth.adapter.ts +│ +├── application/ <- Orquestación de casos de uso y estado reactivo +│ ├── stores/ <- Signals Stores (State Management) +│ │ ├── project.store.ts +│ │ ├── terminal.store.ts +│ │ ├── retro-os.store.ts +│ │ ├── interactive-api.store.ts +│ │ └── auth.store.ts +│ └── services/ <- Lógica de negocio de aplicación +│ ├── terminal-executor.service.ts <- Parser y ejecutor de comandos +│ └── retro-audio.service.ts <- Efectos de sonido retro con Web Audio API +│ +├── presentation/ <- UI Angular (Componentes Standalone OnPush) +│ ├── features/ +│ │ ├── hero-3d/ <- Laptop desarmable en scroll 3D +│ │ │ ├── hero-3d.component.ts +│ │ │ └── hero-3d.component.html +│ │ ├── terminal/ <- Consola Linux interactiva +│ │ │ ├── terminal.component.ts +│ │ │ ├── terminal.component.html +│ │ │ └── components/terminal-line.component.ts +│ │ ├── retro-os/ <- Sistema operativo vintage & mini-juego +│ │ │ ├── retro-os.component.ts +│ │ │ ├── retro-os.component.html +│ │ │ └── components/ +│ │ │ ├── retro-window.component.ts +│ │ │ └── retro-game-canvas.component.ts +│ │ ├── interactive-api/ <- Probador de API en vivo (.NET Playground) +│ │ │ ├── interactive-api.component.ts +│ │ │ └── interactive-api.component.html +│ │ ├── projects/ <- Galería de proyectos con filtros +│ │ │ ├── project-list.component.ts +│ │ │ └── project-card.component.ts +│ │ └── admin/ <- Backoffice protegido por RBAC +│ │ ├── login/login.component.ts +│ │ └── dashboard/admin-dashboard.component.ts +│ └── shared/ <- Componentes UI reutilizables +│ ├── components/ +│ │ ├── zen-button/zen-button.component.ts +│ │ └── zen-badge/zen-badge.component.ts +│ └── pipes/ +│ └── json-pretty.pipe.ts +│ +├── core/ <- Servicios transversales +│ ├── guards/ +│ │ └── auth.guard.ts <- Verificación de token y rol admin +│ ├── interceptors/ +│ │ ├── auth.interceptor.ts <- Inyección de Bearer Token JWT +│ │ └── error.interceptor.ts <- Mapeo de ProblemDetails RFC 7807 +│ └── tokens/ +│ └── api-config.token.ts +│ +├── app.config.ts <- Inyección de dependencias (providers) +└── app.routes.ts <- Rutas principales con Lazy Loading +``` + +--- + +## 5. Implementación Práctica por Capas (Ejemplos Estándar) + +### 5.1 Capa de Dominio (`domain/`) + +Modelos inmutables en TypeScript puro. Cero decoradores `@Injectable` o librerías externas. + +```typescript +// domain/models/interactive-demo.model.ts +export interface ServerTelemetry { + readonly status: string; + readonly framework: string; + readonly databaseEngine: string; + readonly uptimeSeconds: number; + readonly serverTimeUtc: string; + readonly latencyMs?: number; +} + +export interface ApiDemoResult { + readonly endpoint: string; + readonly httpStatus: number; + readonly executionTimeMs: number; + readonly payload: unknown; + readonly isSuccess: boolean; +} +``` + +```typescript +// domain/ports/interactive-demo.port.ts +import { Observable } from 'rxjs'; +import { ApiDemoResult, ServerTelemetry } from '../models/interactive-demo.model'; + +export abstract class InteractiveDemoPort { + abstract getTelemetry(): Observable; + abstract pingServer(): Observable; + abstract executeCustomDemo(demoId: string): Observable; +} +``` + +--- + +### 5.2 Capa de Infraestructura (`infrastructure/`) + +Maneja la comunicación HTTP, DTOs y Mappers hacia el backend .NET. + +```typescript +// infrastructure/dtos/interactive-demo.dto.ts +export interface ServerTelemetryDto { + status: string; + framework: string; + database_engine: string; + uptime_seconds: number; + server_time_utc: string; +} + +export interface ApiExecutionResponseDto { + endpoint: string; + status_code: number; + duration_ms: number; + data: T; +} +``` + +```typescript +// infrastructure/mappers/interactive-demo.mapper.ts +import { ApiDemoResult, ServerTelemetry } from '../../domain/models/interactive-demo.model'; +import { ApiExecutionResponseDto, ServerTelemetryDto } from '../dtos/interactive-demo.dto'; + +export class InteractiveDemoMapper { + static toDomainTelemetry(dto: ServerTelemetryDto, latencyMs: number): ServerTelemetry { + return { + status: dto.status, + framework: dto.framework, + databaseEngine: dto.database_engine, + uptimeSeconds: dto.uptime_seconds, + serverTimeUtc: dto.server_time_utc, + latencyMs + }; + } + + static toDomainDemoResult(dto: ApiExecutionResponseDto): ApiDemoResult { + return { + endpoint: dto.endpoint, + httpStatus: dto.status_code, + executionTimeMs: dto.duration_ms, + payload: dto.data, + isSuccess: dto.status_code >= 200 && dto.status_code < 300 + }; + } +} +``` + +```typescript +// infrastructure/adapters/http-interactive-demo.adapter.ts +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, map } from 'rxjs'; +import { InteractiveDemoPort } from '../../domain/ports/interactive-demo.port'; +import { ApiDemoResult, ServerTelemetry } from '../../domain/models/interactive-demo.model'; +import { ApiExecutionResponseDto, ServerTelemetryDto } from '../dtos/interactive-demo.dto'; +import { InteractiveDemoMapper } from '../mappers/interactive-demo.mapper'; + +@Injectable({ providedIn: 'root' }) +export class HttpInteractiveDemoAdapter implements InteractiveDemoPort { + private readonly http = inject(HttpClient); + private readonly baseUrl = '/api/v1/interactive'; + + getTelemetry(): Observable { + const startTime = performance.now(); + return this.http.get(`${this.baseUrl}/telemetry`).pipe( + map(dto => { + const latency = Math.round(performance.now() - startTime); + return InteractiveDemoMapper.toDomainTelemetry(dto, latency); + }) + ); + } + + pingServer(): Observable { + return this.http.get(`${this.baseUrl}/ping`).pipe( + map(InteractiveDemoMapper.toDomainDemoResult) + ); + } + + executeCustomDemo(demoId: string): Observable { + return this.http.post(`${this.baseUrl}/execute`, { demo_id: demoId }).pipe( + map(InteractiveDemoMapper.toDomainDemoResult) + ); + } +} +``` + +--- + +### 5.3 Capa de Aplicación (`application/stores/`) + +Administración de estado reactivo mediante **Angular Signals**. + +```typescript +// application/stores/interactive-api.store.ts +import { Injectable, computed, inject, signal } from '@angular/core'; +import { InteractiveDemoPort } from '../../domain/ports/interactive-demo.port'; +import { ApiDemoResult, ServerTelemetry } from '../../domain/models/interactive-demo.model'; + +@Injectable({ providedIn: 'root' }) +export class InteractiveApiStore { + private readonly demoPort = inject(InteractiveDemoPort); + + // Signals privadas de estado + private readonly _telemetry = signal(null); + private readonly _lastResult = signal(null); + private readonly _isLoading = signal(false); + private readonly _errorMessage = signal(null); + + // Signals públicas de solo lectura + readonly telemetry = this._telemetry.asReadonly(); + readonly lastResult = this._lastResult.asReadonly(); + readonly isLoading = this._isLoading.asReadonly(); + readonly errorMessage = this._errorMessage.asReadonly(); + + readonly isHealthy = computed(() => this._telemetry()?.status === 'Healthy'); + + loadTelemetry(): void { + this._isLoading.set(true); + this._errorMessage.set(null); + + this.demoPort.getTelemetry().subscribe({ + next: (telemetry) => { + this._telemetry.set(telemetry); + this._isLoading.set(false); + }, + error: (err) => { + this._errorMessage.set(err.message ?? 'No se pudo conectar con el backend .NET'); + this._isLoading.set(false); + } + }); + } + + executePing(): void { + this._isLoading.set(true); + this._errorMessage.set(null); + + this.demoPort.pingServer().subscribe({ + next: (result) => { + this._lastResult.set(result); + this._isLoading.set(false); + }, + error: (err) => { + this._errorMessage.set(err.message ?? 'Error al ejecutar ping'); + this._isLoading.set(false); + } + }); + } +} +``` + +--- + +### 5.4 Capa de Presentación (`presentation/`) + +#### A. Smart Page Component +```typescript +// presentation/features/interactive-api/interactive-api.component.ts +import { Component, OnInit, ChangeDetectionStrategy, inject } from '@angular/core'; +import { InteractiveApiStore } from '../../../application/stores/interactive-api.store'; +import { JsonPrettyPipe } from '../../../shared/pipes/json-pretty.pipe'; + +@Component({ + selector: 'app-interactive-api', + standalone: true, + imports: [JsonPrettyPipe], + templateUrl: './interactive-api.component.html', + styleUrl: './interactive-api.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class InteractiveApiComponent implements OnInit { + protected readonly store = inject(InteractiveApiStore); + + ngOnInit(): void { + this.store.loadTelemetry(); + } + + onTestPing(): void { + this.store.executePing(); + } +} +``` + +#### B. Template con Control Flow Moderno y Estilo Zen +```html + +
+
+
+ + .NET 8 BACKEND // LIVE TESTER +
+

Interacción en Tiempo Real con la API

+
+ +
+ +
+ + @if (store.errorMessage(); as error) { + + } + + @if (store.lastResult(); as result) { +
+
+ GET + {{ result.endpoint }} + + {{ result.httpStatus }} OK + + {{ result.executionTimeMs }} ms +
+ +
{{ result.payload | jsonPretty }}
+
+ } +
+``` + +--- + +## 6. Arquitectura de Módulos Específicos + +### 6.1 Motor de la Terminal Interactiva (`application/services/terminal-executor.service.ts`) +Parser de comandos extensible con mapeo declarativo y llamadas HTTP reales al backend: + +```typescript +import { Injectable, inject } from '@angular/core'; +import { Observable, of, isObservable, map } from 'rxjs'; +import { InteractiveDemoPort } from '../../domain/ports/interactive-demo.port'; + +@Injectable({ providedIn: 'root' }) +export class TerminalExecutorService { + private readonly demoPort = inject(InteractiveDemoPort); + + private readonly commands: Record Observable | string> = { + help: () => `Comandos disponibles: + about - Conoce sobre mi perfil y enfoque arquitectónico + skills - Stack técnico (.NET, Angular, MySQL, Clean Arch) + projects - Listado de proyectos destacados + curl - Ejecuta una petición HTTP en vivo al backend .NET + retro - Abre el sistema operativo vintage y videojuegos + clear - Limpia el buffer de la consola`, + + about: () => 'Bryan // Fullstack Developer especializado en .NET & Angular Clean Architecture.', + skills: () => 'Backend: .NET 8/9, EF Core, MySQL, MediatR, CQRS.\nFrontend: Angular, Signals, OnPush, Canvas 2D.\nPrácticas: Clean Architecture, Ponytail (YAGNI/KISS), TDD.', + clear: () => '__CLEAR__', + retro: () => 'Abriendo entorno ZenOS...', + curl: (args) => { + if (args[0] === '/api/v1/interactive/ping' || args[0] === 'ping') { + return this.demoPort.pingServer().pipe( + map(res => `HTTP ${res.httpStatus} OK (${res.executionTimeMs}ms)\n${JSON.stringify(res.payload, null, 2)}`) + ); + } + return 'Endpoint no reconocido. Prueba con: curl /api/v1/interactive/ping'; + } + }; + + execute(rawInput: string): Observable { + const parts = rawInput.trim().split(/\s+/); + const commandName = parts[0]?.toLowerCase(); + const args = parts.slice(1); + + if (!commandName) return of(''); + const handler = this.commands[commandName]; + if (!handler) return of(`comando no encontrado: ${commandName}. Escribe "help" para ver la lista.`); + + const result = handler(args); + return isObservable(result) ? result : of(result); + } +} +``` + +### 6.2 Motor del Videojuego Canvas Retro (`retro-game-canvas.component.ts`) +Bucle de renderizado nativo en 2D sin librerías pesadas, con limpieza estricta de memoria: + +```typescript +import { Component, AfterViewInit, OnDestroy, ViewChild, ElementRef, ChangeDetectionStrategy, signal } from '@angular/core'; + +@Component({ + selector: 'app-retro-game-canvas', + standalone: true, + template: ``, + styleUrl: './retro-game-canvas.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RetroGameCanvasComponent implements AfterViewInit, OnDestroy { + @ViewChild('gameCanvas') private canvasRef!: ElementRef; + private ctx!: CanvasRenderingContext2D; + private animationFrameId: number | null = null; + private isRunning = false; + + protected readonly score = signal(0); + protected readonly isGameOver = signal(false); + + ngAfterViewInit(): void { + const context = this.canvasRef.nativeElement.getContext('2d'); + if (!context) return; + this.ctx = context; + this.ctx.imageSmoothingEnabled = false; // Pixel-art nítido + this.isRunning = true; + this.gameLoop(); + } + + private gameLoop = (): void => { + if (!this.isRunning) return; + this.update(); + this.render(); + this.animationFrameId = requestAnimationFrame(this.gameLoop); + }; + + private update(): void { + // Actualización de entidades y colisiones del juego + } + + private render(): void { + this.ctx.fillStyle = '#0a0c10'; + this.ctx.fillRect(0, 0, 320, 240); + } + + ngOnDestroy(): void { + this.isRunning = false; + if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); // Cero memory leaks + } + } +} +``` + +### 6.3 Hero 3D con Scroll Desarmable (`hero-3d.component.ts`) +Lectura de scroll pasiva y pausa del render loop mediante `IntersectionObserver`: + +```typescript +import { Component, AfterViewInit, OnDestroy, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core'; + +@Component({ + selector: 'app-hero-3d', + standalone: true, + templateUrl: './hero-3d.component.html', + styleUrl: './hero-3d.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Hero3dComponent implements AfterViewInit, OnDestroy { + @ViewChild('canvasContainer') private containerRef!: ElementRef; + private isVisible = true; + private intersectionObserver?: IntersectionObserver; + protected scrollProgress = 0; + + ngAfterViewInit(): void { + this.intersectionObserver = new IntersectionObserver(([entry]) => { + this.isVisible = entry.isIntersecting; + }, { threshold: 0.05 }); + this.intersectionObserver.observe(this.containerRef.nativeElement); + + window.addEventListener('scroll', this.onScroll, { passive: true }); + } + + private onScroll = (): void => { + if (!this.isVisible) return; + const el = this.containerRef.nativeElement; + const rect = el.getBoundingClientRect(); + this.scrollProgress = Math.min(Math.max(-rect.top / window.innerHeight, 0), 1); + // Aplicar transformación a las piezas 3D según scrollProgress (0.0 a 1.0) + }; + + ngOnDestroy(): void { + window.removeEventListener('scroll', this.onScroll); + this.intersectionObserver?.disconnect(); + } +} +``` + +--- + +## 7. Interceptores y Configuración Global (`app.config.ts`) + +```typescript +// core/interceptors/auth.interceptor.ts +import { HttpInterceptorFn } from '@angular/common/http'; + +export const authInterceptor: HttpInterceptorFn = (req, next) => { + const token = localStorage.getItem('access_token'); + if (token && !req.url.includes('/auth/login')) { + req = req.clone({ + setHeaders: { Authorization: `Bearer ${token}` } + }); + } + return next(req); +}; +``` + +```typescript +// app.config.ts +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter, withComponentInputBinding } from '@angular/router'; +import { provideHttpClient, withInterceptors } from '@angular/common/http'; +import { routes } from './app.routes'; +import { authInterceptor } from './core/interceptors/auth.interceptor'; +import { errorInterceptor } from './core/interceptors/error.interceptor'; +import { InteractiveDemoPort } from './domain/ports/interactive-demo.port'; +import { HttpInteractiveDemoAdapter } from './infrastructure/adapters/http-interactive-demo.adapter'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes, withComponentInputBinding()), + provideHttpClient(withInterceptors([authInterceptor, errorInterceptor])), + // Inversión de dependencias de puertos + { provide: InteractiveDemoPort, useClass: HttpInteractiveDemoAdapter } + ] +}; +``` + +--- + +## 8. Tabla de Anti-Patrones Prohibidos en Frontend + +| Anti-patrón Prohibido | Solución Obligatoria | +|---|---| +| Inyectar `HttpClient` directamente en componentes | Inyectar Store/Facade que consuma Ports implementados en `infrastructure/` | +| Directivas legadas `*ngIf`, `*ngFor` | Sintaxis moderna `@if`, `@for (item of items; track item.id)` | +| `ChangeDetectionStrategy.Default` en componentes | Siempre `ChangeDetectionStrategy.OnPush` | +| Bucle infinito en canvas del juego al cambiar de ruta | Limpiar bucle con `cancelAnimationFrame` en `ngOnDestroy()` | +| Instalar librerías de 1MB para efectos sencillos | Usar Web APIs nativas: Canvas 2D, Web Audio, IntersectionObserver | +| Uso de `any` en respuestas HTTP | Definir DTOs tipados en `infrastructure/dtos/` y mapear a modelos | +| Estado global mutable o variables sueltas | Angular Signals con interfaces inmutables (`readonly`) | diff --git a/.agents/skills/portfolio-ui-design/SKILL.md b/.agents/skills/portfolio-ui-design/SKILL.md new file mode 100644 index 0000000..3163399 --- /dev/null +++ b/.agents/skills/portfolio-ui-design/SKILL.md @@ -0,0 +1,391 @@ +--- +name: portfolio-ui-design +description: Estándar de diseño UI/UX para el Portafolio Profesional Interactivo. Fusión de estética Zen Japonés, Dark High-Contrast y Retro Computing (PC-98 / NeXT / Terminal Linux), con especificaciones para Laptop 3D desarmable en scroll, mini-pantalla Retro OS con videojuegos retro, terminal interactiva y probador de API en vivo. +--- + +# Estándar de Diseño UI/UX — Portafolio Profesional Interactivo (Zen Japonés + Dark + Retro) + +Este documento define las reglas visuales, estéticas, de interacción y diseño de componentes para el **Portafolio Web Profesional Interactivo**. Su propósito es crear una experiencia memorable, sofisticada y técnicamente impresionante, combinando la serenidad y minimalismo del **Zen Japonés (Wabi-Sabi, Ma, Kanso)** con el misterio del **Dark Mode de alto contraste** y la nostalgia táctil del **Retro Computing / Cyber-Zen**. + +--- + +## 1. Filosofía de Diseño — Reglas de Oro + +El agente actúa como un **Director Creativo & Diseñador UI/UX Senior con 10+ años de experiencia** en diseño web interactivo, aplicaciones 3D y sistemas de diseño de alto impacto. + +### 1.1 El Concepto Central: "Cyber-Zen & Retro Elegance" +1. **Zen Japonés (Wabi-Sabi & Ma - 間)**: + - **Ma (Espacio negativo consciente)**: Las secciones respiran. No atiborrar la pantalla. El vacío tiene peso y propósito. + - **Kanso (Simplicidad)**: Eliminar lo superfluo. Cada elemento visual tiene una función de comunicación o interacción. + - **Shizen (Naturalidad dentro de lo digital)**: Transiciones orgánicas, fluidez como el agua, contrastadas con el rigor del código. + - **Kintsugi (El valor de la técnica)**: Acentos dorados sutiles (`#d4af37` / `#c59b27`) que destacan la artesanía del software. +2. **Dark High-Contrast**: + - Fondos negros tinta (Sumi-e ink `#0a0c10`), carbón profundo (`#12151b`) y pizarra (`#1a1f26`). + - Cero grises desteñidos o descoloridos. El texto y los acentos resaltan con legibilidad WCAG AAA. +3. **Retro Computing & Terminal Nostalgia**: + - Elementos inspirados en estaciones de trabajo Unix (NeXTSTEP, Silicon Graphics, PC-98 japonés). + - Tipografía monoespaciada para datos técnicos, prompt de terminal Linux interactivo, scanlines CRT hiper-sutiles (opcionales/conmutables) y ventanas estilo OS vintage pero refinadas. + +### 1.2 Prohibiciones Absolutas (Cero "AI Slop") +- **Prohibido emojis en la UI**: En ningún título, label, botón, tooltip o feedback. +- **Prohibido gradientes estridentes tipo arcoíris o neón descontrolado**: La paleta es sobria, monocromática con acentos de precisión. +- **Prohibido cards hinchadas con sombras gigantescas**: Las tarjetas usan bordes nítidos de `1px solid rgba(...)`, sombras difusas tenues y proporciones elegantes. +- **Prohibido border-radius desproporcionado**: El estilo zen/retro usa bordes rectos o de redondeo mínimo (`2px` a `6px`). Prohibido usar `border-radius: 24px` o píldoras gigantes. +- **Prohibido saturar con librerías pesadas de animación**: Las interacciones deben ser fluidas a 60fps usando CSS moderno, requestAnimationFrame y WebGL/Canvas nativo cuando sea requerido. + +--- + +## 2. Paleta de Colores y Tokens Visuales + +### 2.1 Variables CSS Base (Sumi & Gold Palette) + +```css +:root { + /* Fondos (Sumi-e Ink & Obsidian) */ + --zen-bg-abyss: #07080b; /* Fondo más profundo (canvas del 3D / Landing) */ + --zen-bg-base: #0c0e14; /* Fondo principal de la aplicación */ + --zen-bg-surface: #131720; /* Superficie de cards, ventanas y paneles */ + --zen-bg-elevated: #1a202c; /* Superficies flotantes, dropdowns, tooltips */ + --zen-bg-overlay: rgba(12, 14, 20, 0.85); /* Backdrops y overlays */ + + /* Bordes & Divisores */ + --zen-border-subtle: #1e2533; /* Bordes estándar de división */ + --zen-border-focus: #3d4a63; /* Bordes activos o hover */ + --zen-border-kintsugi: #c59b27; /* Borde acentuado de oro kintsugi */ + + /* Textos & Tipografía */ + --zen-text-primary: #f0f3f8; /* Blanco perla japonés (alta legibilidad) */ + --zen-text-secondary: #9aa5b8; /* Gris pizarra para subtítulos y metadatos */ + --zen-text-muted: #5e697d; /* Gris atenuado para hints y footer */ + --zen-text-gold: #e2b94a; /* Dorado para llamadas de atención selectas */ + + /* Acentos Temáticos */ + --zen-accent-kintsugi: #c59b27; /* Oro kintsugi (artesanía, acción primaria) */ + --zen-accent-matcha: #4e9b6f; /* Verde matcha / Terminal éxito */ + --zen-accent-sakura: #d96f88; /* Rosa sakura atenuado / Alertas o badges */ + --zen-accent-amber: #e69d45; /* Ámbar CRT retro / Warnings */ + --zen-accent-cyan: #38bdf8; /* Azul glacial / API tester */ + + /* Retro CRT & Terminal */ + --term-bg: #090b0e; + --term-prompt: #4e9b6f; /* Verde fósforo zen */ + --term-text: #d8dee9; + --term-cursor: #e2b94a; + + /* Elevaciones & Sombras */ + --zen-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.4); + --zen-shadow-md: 0 4px 16px rgba(0, 0, 0, 0.6); + --zen-shadow-window: 0 10px 30px rgba(0, 0, 0, 0.8), 0 0 1px 1px rgba(255, 255, 255, 0.05); + + /* Curvaturas (Bordes nítidos y elegantes) */ + --zen-radius-xs: 2px; /* Botones retro, inputs */ + --zen-radius-sm: 4px; /* Tarjetas, ventanas */ + --zen-radius-md: 6px; /* Contenedores principales */ +} +``` + +### 2.2 Temas Conmutables (Data-Attributes) + +El usuario puede alternar la paleta visual desde la Terminal (ej: `theme amber`) o desde los ajustes del Retro OS: + +```css +/* Tema 1: Zen Dark (Predeterminado) */ +[data-theme='zen-dark'] { + --zen-bg-base: #0c0e14; + --zen-bg-surface: #131720; + --zen-text-primary: #f0f3f8; + --zen-accent-kintsugi: #c59b27; + --term-prompt: #4e9b6f; +} + +/* Tema 2: Retro CRT Ámbar (Estación de trabajo vintage) */ +[data-theme='retro-amber'] { + --zen-bg-base: #0a0702; + --zen-bg-surface: #140f06; + --zen-border-subtle: #2d1e0a; + --zen-text-primary: #ffb833; + --zen-text-secondary: #cc8e20; + --zen-accent-kintsugi: #ffb833; + --term-bg: #0d0903; + --term-prompt: #ffb833; + --term-text: #ffcf70; + --term-cursor: #ffb833; +} + +/* Tema 3: Matrix / Cyber-Zen Verde */ +[data-theme='matrix-green'] { + --zen-bg-base: #040a06; + --zen-bg-surface: #0a140d; + --zen-border-subtle: #122819; + --zen-text-primary: #33ff77; + --zen-text-secondary: #22aa4e; + --zen-accent-kintsugi: #33ff77; + --term-bg: #040a06; + --term-prompt: #33ff77; + --term-text: #66ff99; + --term-cursor: #33ff77; +} +``` + +--- + +## 3. Tipografía + +El portafolio comunica rigor de ingeniería mediante una cuidada jerarquía tipográfica: + +```css +/* Familias Tipográficas */ +--font-sans: 'Plus Jakarta Sans', 'Inter', -apple-system, sans-serif; +--font-mono: 'JetBrains Mono', 'Fira Code', monospace; +--font-retro: 'VT323', 'Press Start 2P', monospace; /* Para detalles retro específicos */ +``` + +| Elemento | Fuente | Tamaño | Peso | Color | Uso | +|---|---|---|---|---|---| +| Display / Hero Title | Sans | `2.75rem - 3.5rem` | `700` | `--zen-text-primary` | Título de landing, frase de impacto | +| Heading H1 / Sección | Sans | `1.75rem - 2.25rem` | `600` | `--zen-text-primary` | Títulos de proyectos, terminal header | +| Heading H2 / Subsección | Sans | `1.25rem - 1.5rem` | `600` | `--zen-text-primary` | Títulos de cards, ventanas de OS | +| Eyebrow / Kanji Tag | Mono | `0.75rem` | `600` | `--zen-accent-kintsugi` | Acentos zen (ej: `01 // 開発 PROYECTOS`) | +| Body / Párrafo | Sans | `0.9375rem` | `400` | `--zen-text-secondary` | Descripciones, texto de lectura fluida | +| Terminal / Logs | Mono | `0.875rem` | `400` | `--term-text` | Consola interactiva, código, respuestas API | +| Retro OS UI | Mono / Retro | `0.8125rem` | `500` | `--zen-text-primary` | Barras de título de ventanas, menú de juegos | + +--- + +## 4. Componentes Interiores Clave — Especificación Detallada + +### 4.1 Fondo Hero con Laptop 3D Desarmable al Scroll (Exploded View) + +#### Concepto y Comportamiento: +- En la Landing Page, el centro visual es una **laptop estética minimalista** vista en perspectiva isométrica o 3/4. +- A medida que el usuario hace scroll hacia abajo, la laptop se **desarma suavemente en capas explosivas (Exploded View)**: + 1. **Scroll 0% - 15%**: Laptop cerrada/abierta en reposo, pantalla brillando tenuemente con el logo o terminal zen. + 2. **Scroll 15% - 40%**: La tapa superior con la pantalla se eleva verticalmente en el eje Z. + 3. **Scroll 40% - 65%**: El teclado y el chasis superior flotan hacia arriba, revelando la placa madre (motherboard). + 4. **Scroll 65% - 85%**: Los microcomponentes (procesador .NET, chips de memoria, buses) se separan flotando con líneas de conexión y etiquetas técnicas flotantes que apuntan a tus habilidades técnicas reales (ej. "Arquitectura Limpia", "Motor de Signals", "MySQL Optimization"). + 5. **Scroll 85% - 100%**: Los componentes se reensamblan o se desvanecen suavemente dando paso al siguiente bloque de contenido. + +#### Reglas de Implementación Técnica & Rendimiento: +- Utilizar **Three.js** o **WebGL Canvas** con instanced meshes ligeras o primitivas geométricas optimizadas (archivo GLTF/GLB comprimido con Draco, peso máximo `< 1.8 MB`). +- Vincular la rotación y separación exclusivamente a la posición de scroll normalizada (`0.0` a `1.0`) mediante `requestAnimationFrame`. +- **Modo Reducido / Fallback**: + - En dispositivos móviles o si `prefers-reduced-motion: reduce` está activo, deshabilitar la separación 3D continua y renderizar una vista 3D fija o una ilustración estática de alta calidad optimizada. + - Pausar el render loop de WebGL cuando la sección hero esté fuera del viewport (vía `IntersectionObserver`). + +--- + +### 4.2 Terminal Linux Interactiva (Hacker Zen Console) + +#### Concepto: +Una consola interactiva incrustada donde reclutadores y visitantes técnicos pueden ejecutar comandos reales para navegar tu información como si estuvieran en un servidor Unix. + +#### Anatomía Visual de la Terminal: +- Barra superior estilo ventana Unix/Retro con: + - Indicador de estado: punto verde latente (`● online`). + - Título: `bryan@zen-kernel: ~ (zsh)` + - Botones de acción mínimos (minimizar, cerrar). +- Prompt interactivo: `bryan@zen:~$ ` en color matcha `--term-prompt`. +- Cursor parpadeante con animación escalonada `steps(2, start)`. + +#### Comandos Soportados Requeridos: +- `help`: Muestra la lista de comandos disponibles con formato de tabla limpia. +- `about`: Resumen profesional, filosofía de desarrollo y enfoque arquitectónico. +- `skills`: Lista de tecnologías organizadas por Backend (.NET, MySQL), Frontend (Angular, Signals), y Prácticas (Clean Arch, CQRS). +- `projects`: Lista de proyectos destacados con enlaces clickeables. +- `curl /api/ping`: Ejecuta una petición HTTP real al backend .NET y muestra la respuesta con latencia. +- `retro`: Abre la ventana del sistema operativo retro / minijuegos. +- `clear`: Limpia el buffer de la pantalla. +- `sudo`: Mensaje de humor de permisos para administradores. + +#### Estilo CSS de la Terminal: + +```css +.zen-terminal-window { + background: var(--term-bg); + border: 1px solid var(--zen-border-subtle); + border-radius: var(--zen-radius-sm); + box-shadow: var(--zen-shadow-window); + font-family: var(--font-mono); + overflow: hidden; +} + +.zen-terminal-header { + background: #11141a; + padding: 8px 16px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--zen-border-subtle); +} + +.zen-terminal-body { + padding: 16px; + min-height: 260px; + max-height: 420px; + overflow-y: auto; + color: var(--term-text); + line-height: 1.6; +} + +.zen-terminal-prompt { + color: var(--term-prompt); + font-weight: 600; +} +``` + +--- + +### 4.3 Pantalla de Sistema Operativo Retro (ZenOS / Retro Desktop) + +#### Concepto: +Una mini-pantalla o ventana de escritorio que evoca sistemas operativos de los años 90 (estilo NeXTSTEP / PC-98 japonés / System 7): +- Fondo de escritorio con cuadrícula punteada retro o patrón geométrico monocromático. +- Íconos en el escritorio estilo pixel-art minimalista: + - `Juegos.exe` (Abre el catálogo de videojuegos retro). + - `Curriculum.doc` (Visor de experiencia interactiva). + - `Terminal.sh` (Acceso directo a la consola). + - `Ajustes.sys` (Cambio de tema: Zen Dark / Phosphor Amber / Matrix Green). +- Ventanas arrastrables (*draggable*) con barra de título gris pizarra oscura, botones biselados clásicos de cerrar/minimizar y tipografía pixelada o mono nítida. + +#### Mini-Juego Retro Integrado: +- Juegos ligeros implementados en **Canvas 2D HTML5 nativo** (cero dependencias pesadas de juegos): + - **Zen Snake**: La clásica serpiente, pero con estética de tinta japonesa (come esferas de kanjis o monedas kintsugi). + - **Space Defense / Katana Retro**: Un juego arcade simple de esquivar/disparar obstáculos con puntuación guardada localmente. +- Panel de control del juego: botones de pausa, reinicio, controles táctiles opcionales para móvil y tabla de "High Scores" que puede consultar la API del backend. + +--- + +### 4.4 Probador de API en Vivo (Live .NET Playground) + +#### Concepto: +Demuestra de inmediato que no es solo un frontend estático, sino una arquitectura fullstack real en producción. Permite al visitante invocar endpoints públicos controlados de .NET. + +#### Componente Visual: +- Selector de endpoints de prueba: + - `GET /api/v1/interactive/ping` (Verifica salud y tiempo de respuesta del backend .NET). + - `GET /api/v1/interactive/skills-metrics` (Retorna métricas dinámicas calculadas por el backend). + - `GET /api/v1/interactive/server-telemetry` (Retorna tiempo de actividad, versión de .NET y motor de base de datos MySQL). +- Botón de acción: `Ejecutar Endpoint` con feedback de carga elegante. +- Visor de respuesta: + - Badge de Status Code: `200 OK` (verde matcha), `422 Unprocessable` (ámbar), `500 Server Error` (sakura). + - Medidor de latencia: `Tiempo de respuesta: 42 ms`. + - Caja de código JSON formateada con sintaxis coloreada nativa. + +--- + +### 4.5 Panel de Administración RBAC (Backoffice Minimalista Zen) + +#### Concepto: +Vista protegida para que Bryan pueda gestionar sus proyectos, habilidades y contenidos sin desplegar código nuevo. +- Acceso mediante modal discreto (o atajo desde terminal con `login`). +- Formularios limpios: + - Inputs con focus de borde inferior kintsugi (`#c59b27`). + - Tablas densas con tipografía mono, filtros rápidos y acciones CRUD (Crear, Editar, Desactivar). + - Modal de confirmación con diseño sobrio. + +--- + +## 5. Sistema de Espaciado (Grid de 8px) + +Todo padding, margen y separación debe ser múltiplo de `8px` (o submúltiplo de `4px` para elementos microscópicos): + +| Token | Medida | Aplicación | +|---|---|---| +| `--space-1` | `4px` | Separación mínima entre icono y texto | +| `--space-2` | `8px` | Padding interno de inputs, tags y botones compactos | +| `--space-3` | `12px` | Separación de items en listas densas | +| `--space-4` | `16px` | Padding de cards, ventanas y botones estándar | +| `--space-6` | `24px` | Separación entre grupos de campos o elementos de card | +| `--space-8` | `32px` | Padding principal de secciones y modales | +| `--space-12`| `48px` | Separación entre módulos mayores de la página | +| `--space-16`| `64px` | Margen vertical de grandes secciones en desktop | + +--- + +## 6. Movimiento, Animaciones y Microinteracciones + +1. **Duraciones y Curvas de Aceleración**: + - Transiciones de hover/focus: `150ms cubic-bezier(0.4, 0, 0.2, 1)` (rápidas, precisas). + - Apertura de ventanas retro / modales: `200ms cubic-bezier(0, 0, 0.2, 1)` (sin rebotes infantiles). + - Movimiento de scroll 3D: Interpolación lineal suave (*lerp* con factor `0.08` a `0.12`). +2. **Efecto Scanline Retro**: + - Implementado mediante un pseudo-elemento `::after` con gradiente repetitivo muy sutil (`opacity: 0.04`) y `pointer-events: none`. Con interruptor para apagarlo en caso de preferencia del usuario. +3. **Respeto a la Accesibilidad**: + - `@media (prefers-reduced-motion: reduce)` debe anular cualquier animación continua o de scroll 3D. + +### 6.2 Diseño de Sonido Retro (Web Audio API Nativa — 0 KB) + +Para brindar respuesta táctil a la terminal y controles retro sin descargar archivos `.mp3` ni añadir librerías: + +```typescript +export class RetroAudioService { + private ctx: AudioContext | null = null; + + private getContext(): AudioContext { + if (!this.ctx) this.ctx = new (window.AudioContext || (window as any).webkitAudioContext)(); + return this.ctx; + } + + // Clic mecánico sutil al teclear en la terminal (120Hz triángulo) + playKeypress(): void { + const ctx = this.getContext(); + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + osc.type = 'triangle'; + osc.frequency.setValueAtTime(120, ctx.currentTime); + gain.gain.setValueAtTime(0.03, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.03); + osc.connect(gain); + gain.connect(ctx.destination); + osc.start(); + osc.stop(ctx.currentTime + 0.03); + } + + // Beep retro al ejecutar comando con éxito (880Hz senoidal) + playSuccessBeep(): void { + const ctx = this.getContext(); + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + osc.type = 'sine'; + osc.frequency.setValueAtTime(880, ctx.currentTime); + gain.gain.setValueAtTime(0.05, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.08); + osc.connect(gain); + gain.connect(ctx.destination); + osc.start(); + osc.stop(ctx.currentTime + 0.08); + } +} +``` + +--- + +## 7. Límites de Líneas y Modularidad de Componentes UI + +Para permitir un desarrollo visual fluido y expresivo sin caer en el extremo de crear micro-archivos innecesarios para cada detalle, se definen los siguientes umbrales tolerables: + +| Elemento Visual | Líneas Ideales | Máximo Tolerable | Acción al Exceder el Límite | +|---|---|---|---| +| **Plantilla de Componente UI (`.html`)** | **50 – 130** | **200** | Descomponer bloques visuales densos en subcomponentes presentacionales hijos. | +| **Estilos de Componente (`.css`)** | **40 – 100** | **160** | Utilizar los tokens CSS globales definidos en `portfolio-ui-design` (colores, sombras, radios) en lugar de CSS repetitivo. | +| **Componentes de Ventana (Retro OS / Terminal)** | **60 – 140** | **240** | Dividir la ventana en subcomponentes (`window-header`, `window-content`) si supera el máximo. | +| **Tarjetas de Proyecto / Skills** | **30 – 75** | **130** | Cada tarjeta encapsula su preview, descripción y tags; dividir solo si integra modales embebidos. | +| **Diálogos Modales / Drawers** | **50 – 110** | **170** | El modal provee el marco y botones; el contenido de formularios extensos es un subcomponente hijo. | + +### Regla de Descomposición UI: +1. **Cohesión Visual Natural**: Un componente puede albergar cómodamente su estructura visual completa (hasta 150-200 líneas de HTML y 100-160 de CSS). Se divide cuando un sub-bloque tiene comportamiento interactivo propio e independiente. +2. **Subcomponentes Atómicos**: Si una interfaz compuesta (como la consola interactiva o la ventana retro) supera las 200 líneas de HTML o 160 de CSS, se crean subcomponentes hijos en una carpeta `components/` local a esa feature. + +--- + +## 8. Tabla de Anti-Patrones Prohibidos + +| Anti-patrón Prohibido | Solución Correcta en este Portafolio | +|---|---| +| Usar emojis en la interfaz para verse "amigable" | Tipografía limpia, badges de texto plano e iconos SVG monocromáticos precisos | +| Gradientes púrpuras/rosas genéricos de IA | Paleta Sumi-e: fondos carbón profundos con acentos dorados kintsugi y verde matcha | +| Border-radius excesivo tipo píldora (20px+) | Bordes nítidos (`2px` a `6px`) de estilo arquitectónico y retro computing | +| Ventanas retro sobrecargadas que bloquean la navegación | Ventana contenida, redimensionable o minimizable sin entorpecer el flujo principal | +| Modelo 3D pesado que demora 10 segundos en cargar | Geometría optimizada (`< 1.8 MB`), carga diferida (lazy load) y fallback estático | +| Animaciones que saltan y distraen la lectura | Movimiento solo en interacción deliberada o ligado al scroll de forma armónica | +| Falta de contraste en modo oscuro (texto gris sobre gris) | Texto principal perla `#f0f3f8` sobre fondo `#0c0e14` (ratio > 7:1, WCAG AAA) | diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e8b8a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# ============================================================================== +# GITIGNORE GLOBAL — PORTFOLIO (Fullstack .NET + Angular) +# ============================================================================== + +# Node / Angular Frontend +node_modules/ +dist/ +.angular/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# .NET Backend +bin/ +obj/ +*.user +*.userosscache +*.sln.docstates +.vs/ +TestResults/ + +# IDEs & Editores +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Sistema Operativo +.DS_Store +Thumbs.db +desktop.ini + +# Variables de entorno y secretos locales +*.env +*.env.local diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..73b6533 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,12 @@ +# Contexto Global del Proyecto + +## Uso Obligatorio de Skills (Ponytail) +El entorno tiene instalados los skills oficiales del repositorio Ponytail. Como agente, tienes estrictamente prohibido entregar código final sin antes pasarlo por estas herramientas: +1. Cada vez que escribas o modifiques código, debes ejecutar de forma autónoma el skill `ponytail-review` sobre tu propio trabajo. +2. Si el skill de Ponytail detecta sobreingeniería, abstracciones innecesarias o código que podría resolverse con la librería estándar de .NET o Angular, debes refactorizarlo inmediatamente antes de darme la respuesta final. + +## Workflows y Habilidades Locales +Para saber la estructura exacta del código, lee obligatoriamente las directrices en las siguientes rutas según la tarea: +- Backend (.NET Clean Arch): `.agents/skills/portfolio-backend/` +- Frontend (Angular): `.agents/skills/portfolio-frontend/` +- Diseño UI: `.agents/skills/portfolio-ui-design/` \ No newline at end of file diff --git a/database/schema.sql b/database/schema.sql new file mode 100644 index 0000000..4f017b8 --- /dev/null +++ b/database/schema.sql @@ -0,0 +1,220 @@ +-- ============================================================================== +-- BASE DE DATOS: portfolio_db +-- MOTOR: MySQL 8.0+ / MariaDB 10.4+ / InnoDB +-- CHARSET: utf8mb4 / utf8mb4_unicode_ci +-- DESCRIPCIÓN: Esquema relacional minimalista y sin duplicación para +-- Portafolio Interactivo (Zen + Retro + Clean Arch .NET & Angular) +-- NOTA DBEAVER: Ejecutar todo el archivo con Alt + X (Ejecutar Script SQL / ▶▶). +-- No usar Ctrl + Enter sobre múltiples sentencias (error 1064). +-- ============================================================================== + +CREATE DATABASE IF NOT EXISTS `portfolio_db` + CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci; + +USE `portfolio_db`; + +-- ------------------------------------------------------------------------------ +-- 1. TABLA: users (Autenticación y RBAC del Administrador) +-- Justificación: Almacena la cuenta del propietario (Bryan) para acceder al panel +-- administrativo. No se crean tablas complejas de 'roles' o 'permisos' porque +-- solo existen 2 roles en el sistema (Admin y Visitor). Un campo 'role' es la +-- solución más limpia y minimalista (Ponytail). +-- ------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS `users` ( + `id` CHAR(36) NOT NULL, + `username` VARCHAR(50) NOT NULL, + `email` VARCHAR(100) NOT NULL, + `password_hash` VARCHAR(255) NOT NULL, + `role` VARCHAR(20) NOT NULL DEFAULT 'Admin', + `created_at_utc` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at_utc` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uq_users_username` (`username`), + UNIQUE KEY `uq_users_email` (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ------------------------------------------------------------------------------ +-- 2. TABLA: refresh_tokens (Rotación segura de Tokens JWT) +-- Justificación: Permite mantener la sesión de administración de Bryan de forma +-- segura sin almacenar JWTs de larga duración. Si se detecta un token revocado, +-- se invalida la cadena. Relación 1:N con 'users' con borrado en cascada. +-- ------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS `refresh_tokens` ( + `id` CHAR(36) NOT NULL, + `user_id` CHAR(36) NOT NULL, + `token_hash` VARCHAR(255) NOT NULL, + `expires_at_utc` DATETIME NOT NULL, + `is_revoked` BOOLEAN NOT NULL DEFAULT FALSE, + `created_at_utc` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `replaced_by_token` VARCHAR(255) NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_refresh_tokens_user` (`user_id`), + KEY `idx_refresh_tokens_validation` (`user_id`, `is_revoked`, `expires_at_utc`), + CONSTRAINT `fk_refresh_tokens_user` FOREIGN KEY (`user_id`) + REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ------------------------------------------------------------------------------ +-- 3. TABLA: skills (Catálogo de Habilidades Técnicas) +-- Justificación: Administradas por Bryan vía CRUD. Se renderizan en el frontend +-- y se consultan desde el comando 'skills' de la terminal Linux interactiva. +-- Normaliza los nombres técnicos para que no se dupliquen strings en los proyectos. +-- ------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS `skills` ( + `id` CHAR(36) NOT NULL, + `name` VARCHAR(50) NOT NULL, + `category` ENUM('Backend', 'Frontend', 'Database', 'DevOps', 'Architecture') NOT NULL, + `icon_slug` VARCHAR(50) NULL DEFAULT NULL, + `proficiency_percentage` TINYINT UNSIGNED NOT NULL DEFAULT 85, + `display_order` SMALLINT UNSIGNED NOT NULL DEFAULT 0, + `is_featured` BOOLEAN NOT NULL DEFAULT TRUE, + `created_at_utc` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uq_skills_name` (`name`), + KEY `idx_skills_category_order` (`category`, `display_order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ------------------------------------------------------------------------------ +-- 4. TABLA: projects (Proyectos del Portafolio) +-- Justificación: Catálogo de proyectos destacados y proyectos generales. Permite +-- a Bryan crear/editar proyectos sin tocar código. Contiene slugs amigables para +-- URLs y terminal (`projects show [slug]`), enlaces a repositorios y demos en vivo. +-- ------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS `projects` ( + `id` CHAR(36) NOT NULL, + `slug` VARCHAR(100) NOT NULL, + `title` VARCHAR(150) NOT NULL, + `short_description` VARCHAR(255) NOT NULL, + `full_description` TEXT NOT NULL, + `repository_url` VARCHAR(255) NOT NULL, + `live_demo_url` VARCHAR(255) NULL DEFAULT NULL, + `thumbnail_url` VARCHAR(255) NULL DEFAULT NULL, + `is_featured` BOOLEAN NOT NULL DEFAULT FALSE, + `display_order` SMALLINT UNSIGNED NOT NULL DEFAULT 0, + `created_at_utc` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at_utc` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uq_projects_slug` (`slug`), + KEY `idx_projects_featured_order` (`is_featured`, `display_order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ------------------------------------------------------------------------------ +-- 5. TABLA: project_skills (Tabla Intermedia N:M Proyectos <-> Habilidades) +-- Justificación: Evita la duplicación de datos (3FN). Un proyecto usa muchas +-- tecnologías y una tecnología está en muchos proyectos. Permite filtrar +-- proyectos por tecnología con indexación real sin recurrir a campos de texto +-- plano o arrays desnormalizados difíciles de consultar. +-- ------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS `project_skills` ( + `project_id` CHAR(36) NOT NULL, + `skill_id` CHAR(36) NOT NULL, + PRIMARY KEY (`project_id`, `skill_id`), + KEY `idx_project_skills_skill` (`skill_id`), + CONSTRAINT `fk_project_skills_project` FOREIGN KEY (`project_id`) + REFERENCES `projects` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_project_skills_skill` FOREIGN KEY (`skill_id`) + REFERENCES `skills` (`id`) ON DELETE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ------------------------------------------------------------------------------ +-- 6. TABLA: retro_scores (Tabla de Puntuaciones de Minijuegos Retro) +-- Justificación: Soporta la interactividad del videojuego retro en la ventana de +-- Retro OS. Los visitantes pueden ingresar su tag arcade de 3-4 letras (ej. 'BRY', +-- 'JDO') y competir por el Top 10 en tiempo real. Proporciona un endpoint público +-- interactivo perfecto para demostrar llamadas reales al backend .NET y MySQL. +-- ------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS `retro_scores` ( + `id` CHAR(36) NOT NULL, + `player_tag` VARCHAR(4) NOT NULL, + `game_code` VARCHAR(20) NOT NULL DEFAULT 'ZEN_SNAKE', + `score` INT UNSIGNED NOT NULL, + `created_at_utc` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_retro_leaderboard` (`game_code`, `score` DESC, `created_at_utc` ASC) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ------------------------------------------------------------------------------ +-- 7. TABLA: interactive_messages (Libro de Visitas / Mensajes Interactivos) +-- Justificación: Permite a reclutadores y visitantes dejar una firma o mensaje +-- desde el comando interactivo de la terminal Linux (`guestbook sign "Mensaje"`) +-- o el probador de API en vivo. Demuestra operaciones de escritura inmediatas +-- y controladas en el backend con almacenamiento persistente. +-- ------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS `interactive_messages` ( + `id` CHAR(36) NOT NULL, + `sender_name` VARCHAR(60) NOT NULL, + `sender_contact` VARCHAR(100) NULL DEFAULT NULL, + `message` VARCHAR(500) NOT NULL, + `origin` ENUM('Terminal', 'WebForm', 'ApiTester') NOT NULL DEFAULT 'Terminal', + `is_approved` BOOLEAN NOT NULL DEFAULT TRUE, + `created_at_utc` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_messages_approved_date` (`is_approved`, `created_at_utc` DESC) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ============================================================================== +-- SEED DATA INICIAL (Datos de prueba para arranque inmediato) +-- ============================================================================== + +-- 1. Usuario Administrador Inicial (Bryan) +-- Contraseña temporal por defecto: 'Admin123*!' (hasheada con BCrypt/PBKDF2 para prueba) +INSERT INTO `users` (`id`, `username`, `email`, `password_hash`, `role`) +VALUES ( + '11111111-1111-1111-1111-111111111111', + 'bryan', + 'bryan@portfolio.local', + '$2a$11$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy', + 'Admin' +) ON DUPLICATE KEY UPDATE `username` = `username`; + +-- 2. Habilidades Iniciales +INSERT INTO `skills` (`id`, `name`, `category`, `icon_slug`, `proficiency_percentage`, `display_order`, `is_featured`) +VALUES + ('22222222-0001-0000-0000-000000000001', '.NET 8/9 / C#', 'Backend', 'dotnet', 95, 1, TRUE), + ('22222222-0002-0000-0000-000000000002', 'Clean Architecture & CQRS', 'Architecture', 'architecture', 95, 2, TRUE), + ('22222222-0003-0000-0000-000000000003', 'Angular & Signals', 'Frontend', 'angular', 90, 3, TRUE), + ('22222222-0004-0000-0000-000000000004', 'MySQL / EF Core', 'Database', 'mysql', 90, 4, TRUE), + ('22222222-0005-0000-0000-000000000005', 'Docker & CI/CD', 'DevOps', 'docker', 85, 5, TRUE) +ON DUPLICATE KEY UPDATE `name` = `name`; + +-- 3. Proyecto Destacado Inicial +INSERT INTO `projects` (`id`, `slug`, `title`, `short_description`, `full_description`, `repository_url`, `live_demo_url`, `is_featured`, `display_order`) +VALUES ( + '33333333-0001-0000-0000-000000000001', + 'portfolio-interactivo-zen', + 'Portafolio Profesional Interactivo', + 'Portafolio interactivo con estética Zen Dark, 3D Hero desarmable, terminal Linux y backend .NET Clean Architecture.', + 'Sistema completo desarrollado con .NET 8/9, EF Core, MySQL, y Angular con Clean Architecture y Signals reactivos.', + 'https://github.com/bryan/portfolio', + 'https://portfolio.bryan.dev', + TRUE, + 1 +) ON DUPLICATE KEY UPDATE `slug` = `slug`; + +-- 4. Vinculación Proyecto - Habilidades +INSERT INTO `project_skills` (`project_id`, `skill_id`) +VALUES + ('33333333-0001-0000-0000-000000000001', '22222222-0001-0000-0000-000000000001'), + ('33333333-0001-0000-0000-000000000001', '22222222-0002-0000-0000-000000000002'), + ('33333333-0001-0000-0000-000000000001', '22222222-0003-0000-0000-000000000003'), + ('33333333-0001-0000-0000-000000000001', '22222222-0004-0000-0000-000000000004') +ON DUPLICATE KEY UPDATE `project_id` = `project_id`; + +-- 5. Scores de Prueba para el Minijuego Retro +INSERT INTO `retro_scores` (`id`, `player_tag`, `game_code`, `score`) +VALUES + (UUID(), 'ZEN', 'ZEN_SNAKE', 1450), + (UUID(), 'NEO', 'ZEN_SNAKE', 1120), + (UUID(), 'BRY', 'ZEN_SNAKE', 980) +ON DUPLICATE KEY UPDATE `player_tag` = `player_tag`; + +-- 6. Mensaje de Bienvenida en el Libro de Visitas +INSERT INTO `interactive_messages` (`id`, `sender_name`, `sender_contact`, `message`, `origin`) +VALUES ( + UUID(), + 'Kernel Bot', + 'system@zen.os', + 'Bienvenido a la terminal interactiva. Ejecuta "help" para comenzar.', + 'Terminal' +) ON DUPLICATE KEY UPDATE `sender_name` = `sender_name`; diff --git a/src/frontend/.editorconfig b/src/frontend/.editorconfig new file mode 100644 index 0000000..f166060 --- /dev/null +++ b/src/frontend/.editorconfig @@ -0,0 +1,17 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single +ij_typescript_use_double_quotes = false + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/src/frontend/.gitignore b/src/frontend/.gitignore new file mode 100644 index 0000000..b1d225e --- /dev/null +++ b/src/frontend/.gitignore @@ -0,0 +1,43 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings +__screenshots__/ + +# System files +.DS_Store +Thumbs.db diff --git a/src/frontend/README.md b/src/frontend/README.md new file mode 100644 index 0000000..caae9dc --- /dev/null +++ b/src/frontend/README.md @@ -0,0 +1,59 @@ +# PortfolioFrontend + +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.26. + +## Development server + +To start a local development server, run: + +```bash +ng serve +``` + +Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. + +## Code scaffolding + +Angular CLI includes powerful code scaffolding tools. To generate a new component, run: + +```bash +ng generate component component-name +``` + +For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: + +```bash +ng generate --help +``` + +## Building + +To build the project run: + +```bash +ng build +``` + +This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. + +## Running unit tests + +To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: + +```bash +ng test +``` + +## Running end-to-end tests + +For end-to-end (e2e) testing, run: + +```bash +ng e2e +``` + +Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. + +## Additional Resources + +For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/src/frontend/angular.json b/src/frontend/angular.json new file mode 100644 index 0000000..b7a03f1 --- /dev/null +++ b/src/frontend/angular.json @@ -0,0 +1,97 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "portfolio-frontend": { + "projectType": "application", + "schematics": {}, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "browser": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + }, + { + "glob": "**/*", + "input": "src/assets", + "output": "assets" + } + ], + "styles": [ + "src/styles.css" + ] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "8kB", + "maximumError": "16kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "configurations": { + "production": { + "buildTarget": "portfolio-frontend:build:production" + }, + "development": { + "buildTarget": "portfolio-frontend:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular/build:extract-i18n" + }, + "test": { + "builder": "@angular/build:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.css" + ] + } + } + } + } + } +} diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json new file mode 100644 index 0000000..e7cb493 --- /dev/null +++ b/src/frontend/package-lock.json @@ -0,0 +1,9065 @@ +{ + "name": "portfolio-frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "portfolio-frontend", + "version": "0.0.0", + "dependencies": { + "@angular/common": "^20.3.0", + "@angular/compiler": "^20.3.0", + "@angular/core": "^20.3.0", + "@angular/forms": "^20.3.0", + "@angular/platform-browser": "^20.3.0", + "@angular/router": "^20.3.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.15.0" + }, + "devDependencies": { + "@angular/build": "^20.3.26", + "@angular/cli": "^20.3.26", + "@angular/compiler-cli": "^20.3.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.9.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.9.2" + } + }, + "node_modules/@algolia/abtesting": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.1.0.tgz", + "integrity": "sha512-sEyWjw28a/9iluA37KLGu8vjxEIlb60uxznfTUmXImy7H5NvbpSO6yYgmgH5KiD7j+zTUUihiST0jEP12IoXow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.35.0.tgz", + "integrity": "sha512-uUdHxbfHdoppDVflCHMxRlj49/IllPwwQ2cQ8DLC4LXr3kY96AHBpW0dMyi6ygkn2MtFCc6BxXCzr668ZRhLBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.35.0.tgz", + "integrity": "sha512-SunAgwa9CamLcRCPnPHx1V2uxdQwJGqb1crYrRWktWUdld0+B2KyakNEeVn5lln4VyeNtW17Ia7V7qBWyM/Skw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.35.0.tgz", + "integrity": "sha512-ipE0IuvHu/bg7TjT2s+187kz/E3h5ssfTtjpg1LbWMgxlgiaZIgTTbyynM7NfpSJSKsgQvCQxWjGUO51WSCu7w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.35.0.tgz", + "integrity": "sha512-UNbCXcBpqtzUucxExwTSfAe8gknAJ485NfPN6o1ziHm6nnxx97piIbcBQ3edw823Tej2Wxu1C0xBY06KgeZ7gA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.35.0.tgz", + "integrity": "sha512-/KWjttZ6UCStt4QnWoDAJ12cKlQ+fkpMtyPmBgSS2WThJQdSV/4UWcqCUqGH7YLbwlj3JjNirCu3Y7uRTClxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.35.0.tgz", + "integrity": "sha512-8oCuJCFf/71IYyvQQC+iu4kgViTODbXDk3m7yMctEncRSRV+u2RtDVlpGGfPlJQOrAY7OONwJlSHkmbbm2Kp/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.35.0.tgz", + "integrity": "sha512-FfmdHTrXhIduWyyuko1YTcGLuicVbhUyRjO3HbXE4aP655yKZgdTIfMhZ/V5VY9bHuxv/fGEh3Od1Lvv2ODNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.35.0.tgz", + "integrity": "sha512-gPzACem9IL1Co8mM1LKMhzn1aSJmp+Vp434An4C0OBY4uEJRcqsLN3uLBlY+bYvFg8C8ImwM9YRiKczJXRk0XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.35.0.tgz", + "integrity": "sha512-w9MGFLB6ashI8BGcQoVt7iLgDIJNCn4OIu0Q0giE3M2ItNrssvb8C0xuwJQyTy1OFZnemG0EB1OvXhIHOvQwWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.35.0.tgz", + "integrity": "sha512-AhrVgaaXAb8Ue0u2nuRWwugt0dL5UmRgS9LXe0Hhz493a8KFeZVUE56RGIV3hAa6tHzmAV7eIoqcWTQvxzlJeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.35.0.tgz", + "integrity": "sha512-diY415KLJZ6x1Kbwl9u96Jsz0OstE3asjXtJ9pmk1d+5gPuQ5jQyEsgC+WmEXzlec3iuVszm8AzNYYaqw6B+Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.35.0.tgz", + "integrity": "sha512-uydqnSmpAjrgo8bqhE9N1wgcB98psTRRQXcjc4izwMB7yRl9C8uuAQ/5YqRj04U0mMQ+fdu2fcNF6m9+Z1BzDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.35.0.tgz", + "integrity": "sha512-RgLX78ojYOrThJHrIiPzT4HW3yfQa0D7K+MQ81rhxqaNyNBu4F1r+72LNHYH/Z+y9I1Mrjrd/c/Ue5zfDgAEjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.2003.35", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.35.tgz", + "integrity": "sha512-fuiccQamG0SBfuKRSA8uAoALI4VprZAWZbwwOGmpMaYrxoN9E4CgicR5hQUEEsp9IzMtuRvFpJPorudv2xzGMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "20.3.35", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/core": { + "version": "20.3.35", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.35.tgz", + "integrity": "sha512-RefMFJA7qRBxEDu3a6bVNK9o5vcHXxN8xKaCzENBrOIO6XamthoGMZIJrIT213KpHoEbLTcJjESqGFCpYmY6oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.18.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.4", + "rxjs": "7.8.2", + "source-map": "0.7.6" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "20.3.35", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.35.tgz", + "integrity": "sha512-7lzrY5pYCTvL9dR/pi4fzAl1BS8PdiplTd+QNFEJv7/rZWTmqt+7rmDHm/8vEl1aYEFmBckzb3PMK+NBMuQkaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "20.3.35", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "8.2.0", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/build": { + "version": "20.3.35", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.35.tgz", + "integrity": "sha512-NwVaMXFZRfmpwodXwwoPXJw6eDZtG+HuBEjGMwyvGoGSMUc12MxKvgTash94pXqhe9SiwZTRgm3oVwrjG//2OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.2003.35", + "@babel/core": "7.29.7", + "@babel/helper-annotate-as-pure": "7.27.3", + "@babel/helper-split-export-declaration": "7.24.7", + "@inquirer/confirm": "5.1.14", + "@vitejs/plugin-basic-ssl": "2.1.0", + "beasties": "0.3.5", + "browserslist": "^4.23.0", + "esbuild": "0.28.1", + "https-proxy-agent": "7.0.6", + "istanbul-lib-instrument": "6.0.3", + "jsonc-parser": "3.3.1", + "listr2": "9.0.1", + "magic-string": "0.30.17", + "mrmime": "2.0.1", + "parse5-html-rewriting-stream": "8.0.0", + "picomatch": "4.0.4", + "piscina": "5.2.0", + "rollup": "4.59.0", + "sass": "1.90.0", + "semver": "7.7.2", + "source-map-support": "0.5.21", + "tinyglobby": "0.2.14", + "vite": "7.3.6", + "watchpack": "2.4.4" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "lmdb": "3.4.2" + }, + "peerDependencies": { + "@angular/compiler": "^20.0.0", + "@angular/compiler-cli": "^20.0.0", + "@angular/core": "^20.0.0", + "@angular/localize": "^20.0.0", + "@angular/platform-browser": "^20.0.0", + "@angular/platform-server": "^20.0.0", + "@angular/service-worker": "^20.0.0", + "@angular/ssr": "^20.3.35", + "karma": "^6.4.0", + "less": "^4.2.0", + "ng-packagr": "^20.0.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "tslib": "^2.3.0", + "typescript": ">=5.8 <6.0", + "vitest": "^3.1.1" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + }, + "@angular/localize": { + "optional": true + }, + "@angular/platform-browser": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "@angular/ssr": { + "optional": true + }, + "karma": { + "optional": true + }, + "less": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@angular/cli": { + "version": "20.3.35", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.35.tgz", + "integrity": "sha512-pKzfR4FslcKDELiUQD2zu1obx5A7pTZC3l2/TgFtVdx0N/cUZz5TlFTa6CQaq8r6P6/4ZchzCIVNGUjkkLfjxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.2003.35", + "@angular-devkit/core": "20.3.35", + "@angular-devkit/schematics": "20.3.35", + "@inquirer/prompts": "7.8.2", + "@listr2/prompt-adapter-inquirer": "3.0.1", + "@modelcontextprotocol/sdk": "1.30.0", + "@schematics/angular": "20.3.35", + "@yarnpkg/lockfile": "1.1.0", + "algoliasearch": "5.35.0", + "ini": "5.0.0", + "jsonc-parser": "3.3.1", + "listr2": "9.0.1", + "npm-package-arg": "13.0.0", + "pacote": "21.5.1", + "resolve": "1.22.10", + "semver": "7.7.2", + "yargs": "18.0.0", + "zod": "4.1.13" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "20.3.30", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.30.tgz", + "integrity": "sha512-XDeT5lshP2tpl9/6IAPmJ5M4zzOTJSUtRxfkOrxmNnNCH4+vbBv1RYs2bnUDdZNsFxRMrV/ZvpZfO7dZTAie+Q==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/core": "20.3.30", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "20.3.30", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.30.tgz", + "integrity": "sha512-0nvYF7u4AmSFGWUbkm4xWgZ5m4wLr1wo6YbflZzKZfxQ2EGOqEF0jgdBoB/oSU6cFi8EqgQ+vY24g8wAKAwxmA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "20.3.30", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.3.30.tgz", + "integrity": "sha512-wWxScyHv3LvjEEJwOBiWSs+zRsCFBS90mGdogMmI7k2A8P6uT/zRDf9wDy1fn2kR6wteRJbGjb88eJecSc126g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.29.7", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^4.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^18.0.0" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/compiler": "20.3.30", + "typescript": ">=5.8 <6.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular/core": { + "version": "20.3.30", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.30.tgz", + "integrity": "sha512-dSjere7z2QkIipxDheRrkz3WwRUMcTrzPYt+P+OB8v/XyUzi2ynjP19LHhEAYzYmcR8J6/S1pe3bsyKKE1HCMg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/compiler": "20.3.30", + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.15.0" + }, + "peerDependenciesMeta": { + "@angular/compiler": { + "optional": true + }, + "zone.js": { + "optional": true + } + } + }, + "node_modules/@angular/forms": { + "version": "20.3.30", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.30.tgz", + "integrity": "sha512-rdpUQf6pJb8b5pZ196BwEggSl0NJYWrO0QpGvwIMYDj5bwayz5nC4elSmgnqc9FAcPlenXam/fj7vaS+w3ioMg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/common": "20.3.30", + "@angular/core": "20.3.30", + "@angular/platform-browser": "20.3.30", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "20.3.30", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.30.tgz", + "integrity": "sha512-K5UlNV6Dg6BesCIDtrXgFLERZY9YYCD8VzYxBEHZn1lVD4KpQL0Qk8NNFAx0T7ikfSuSyOrXA3RGxQobYdjH2g==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/animations": "20.3.30", + "@angular/common": "20.3.30", + "@angular/core": "20.3.30" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/router": { + "version": "20.3.30", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.30.tgz", + "integrity": "sha512-QZcKFVW/WQGfBg8fQa/UUD56TojO1Wm+EBRDL6FXXGNo6Tg84mRNHgy49EtmlzfiCC94DSmAgWg6z1CX0rGSNw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/common": "20.3.30", + "@angular/core": "20.3.30", + "@angular/platform-browser": "20.3.30", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@gar/promise-retry": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.3.tgz", + "integrity": "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@hono/node-server": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.1.tgz", + "integrity": "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.14.tgz", + "integrity": "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.2.tgz", + "integrity": "sha512-nqhDw2ZcAUrKNPwhjinJny903bRhI0rQhiDz1LksjeRxqa36i3l75+4iXbOy0rlDpLJGxqtgoPavQjmmyS5UJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.2.1", + "@inquirer/confirm": "^5.1.14", + "@inquirer/editor": "^4.2.17", + "@inquirer/expand": "^4.0.17", + "@inquirer/input": "^4.2.1", + "@inquirer/number": "^3.0.17", + "@inquirer/password": "^4.0.17", + "@inquirer/rawlist": "^4.1.5", + "@inquirer/search": "^3.1.0", + "@inquirer/select": "^4.3.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@listr2/prompt-adapter-inquirer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.1.tgz", + "integrity": "sha512-3XFmGwm3u6ioREG+ynAQB7FoxfajgQnMhIu8wC5eo/Lsih4aKDg0VuIMGaOsYn7hJSJagSeaD4K8yfpkEoDEmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 8", + "listr2": "9.0.1" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.4.2.tgz", + "integrity": "sha512-NK80WwDoODyPaSazKbzd3NEJ3ygePrkERilZshxBViBARNz21rmediktGHExoj9n5t9+ChlgLlxecdFKLCuCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.4.2.tgz", + "integrity": "sha512-zevaowQNmrp3U7Fz1s9pls5aIgpKRsKb3dZWDINtLiozh3jZI9fBrI19lYYBxqdyiIyNdlyiidPnwPShj4aK+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.4.2.tgz", + "integrity": "sha512-OmHCULY17rkx/RoCoXlzU7LyR8xqrksgdYWwtYa14l/sseezZ8seKWXcogHcjulBddER5NnEFV4L/Jtr2nyxeg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.4.2.tgz", + "integrity": "sha512-ZBEfbNZdkneebvZs98Lq30jMY8V9IJzckVeigGivV7nTHJc+89Ctomp1kAIWKlwIG0ovCDrFI448GzFPORANYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.4.2.tgz", + "integrity": "sha512-vL9nM17C77lohPYE4YaAQvfZCSVJSryE4fXdi8M7uWPBnU+9DJabgKVAeyDb84ZM2vcFseoBE4/AagVtJeRE7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-arm64": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.4.2.tgz", + "integrity": "sha512-SXWjdBfNDze4ZPeLtYIzsIeDJDJ/SdsA0pEXcUBayUIMO0FQBHfVZZyHXQjjHr4cvOAzANBgIiqaXRwfMhzmLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.4.2.tgz", + "integrity": "sha512-IY+r3bxKW6Q6sIPiMC0L533DEfRJSXibjSI3Ft/w9Q8KQBNqEIvUFXt+09wV8S5BRk0a8uSF19YWxuRwEfI90g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", + "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/nice": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", + "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz", + "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz", + "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz", + "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz", + "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz", + "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz", + "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz", + "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz", + "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz", + "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz", + "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz", + "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz", + "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz", + "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-openharmony-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz", + "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz", + "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz", + "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz", + "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/agent": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.2.tgz", + "integrity": "sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==", + "dev": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^11.2.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/fs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", + "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz", + "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "ini": "^6.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git/node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/git/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-4.0.0.tgz", + "integrity": "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^5.0.0", + "npm-normalize-package-bin": "^5.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-5.0.0.tgz", + "integrity": "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.5.tgz", + "integrity": "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "glob": "^13.0.0", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^5.0.0", + "proc-log": "^6.0.0", + "semver": "^7.5.3", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@npmcli/package-json/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/package-json/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/package-json/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/package-json/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", + "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-10.0.4.tgz", + "integrity": "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "node-gyp": "^12.1.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@schematics/angular": { + "version": "20.3.35", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.35.tgz", + "integrity": "sha512-og7zaTVyUYAiXrVqax8+/Uh9K/FExRQqoMC/jQ5X+ZNgmR3MfM+a5aEKRGNZxkUPeJReXF48FCFkBxxt6aCSWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "20.3.35", + "@angular-devkit/schematics": "20.3.35", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@sigstore/bundle": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", + "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/core": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.2.1.tgz", + "integrity": "sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.2.tgz", + "integrity": "sha512-SQqvFMt4V78fdjcDdYX6HbiVSOR4QK3ZgwCa2KOsopAgPIHy1rU5UDUmzLl02r5oyyaYcYHR1hpwDRk/yUe+Mw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.1.tgz", + "integrity": "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@gar/promise-retry": "^1.0.2", + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.4", + "proc-log": "^6.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.2.tgz", + "integrity": "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.1.tgz", + "integrity": "sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.1", + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", + "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^10.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@tufjs/models/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jasmine": { + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.15.tgz", + "integrity": "sha512-ZAC8KjmV2MJxbNTrwXFN+HKeajpXQZp6KpPiR6Aa4XvaEnjP6qh23lL/Rqb7AYzlp3h/rcwDrQ7Gg7q28cQTQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.4.0.tgz", + "integrity": "sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.0.tgz", + "integrity": "sha512-dOxxrhgyDIEUADhb/8OlV9JIqYLgos03YorAueTIeOUskLJSEsfwCByjbu98ctXitUN3znXKp0bYD/WHSudCeA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0" + } + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/algoliasearch": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.35.0.tgz", + "integrity": "sha512-Y+moNhsqgLmvJdgTsO4GZNgsaDWv8AOGAaPeIeHKlDn/XunoAqYbA+XNpBd1dW8GOXAUDyxC9Rxc7AV4kpFcIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.1.0", + "@algolia/client-abtesting": "5.35.0", + "@algolia/client-analytics": "5.35.0", + "@algolia/client-common": "5.35.0", + "@algolia/client-insights": "5.35.0", + "@algolia/client-personalization": "5.35.0", + "@algolia/client-query-suggestions": "5.35.0", + "@algolia/client-search": "5.35.0", + "@algolia/ingestion": "1.35.0", + "@algolia/monitoring": "1.35.0", + "@algolia/recommend": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.20.tgz", + "integrity": "sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/beasties": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.3.5.tgz", + "integrity": "sha512-NaWu+f4YrJxEttJSm16AzMIFtVldCvaJ68b1L098KpqXmxt9xOLtKoLkKxb8ekhOrLqEJAbvT6n6SEvB/sac7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "css-select": "^6.0.0", + "css-what": "^7.0.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "htmlparser2": "^10.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.49", + "postcss-media-query-parser": "^0.2.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.4.tgz", + "integrity": "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^5.0.0", + "fs-minipass": "^3.0.0", + "glob": "^13.0.0", + "lru-cache": "^11.1.0", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/cacache/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/connect/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", + "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^7.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "nth-check": "^2.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", + "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.416", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.416.tgz", + "integrity": "sha512-K6bvB2BjnNrugtIih6ewlbBI9DXa976jIdiIlRLHhBoEI9a4JaQjjHyF+A1IQI543aQYR4LnmOrT/K5fZj0aPA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.9", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.9.tgz", + "integrity": "sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "@types/ws": "^8.5.12", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.21.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz", + "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "punycode": "^1.4.1", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.2.tgz", + "integrity": "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.6.tgz", + "integrity": "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.13.5", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.5.tgz", + "integrity": "sha512-O6+/eCYRkzzzy0rPWwKLiGBR1nFuUPZynnwjxN1MBA62NNqbT0wQEzQyK2gSO5yDIDB336sXQleAhOHrzlYyKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hosted-git-info": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", + "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore-walk": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz", + "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^10.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/ignore-walk/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/ip-address": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz", + "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jasmine-core": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.9.0.tgz", + "integrity": "sha512-OMUvF1iI6+gSRYOhMrH4QYothVLN9C3EJ6wm4g7zLJlnaTl8zbaPOr0bTw70l7QxkoM7sVFOWo83u9B2Fe2Zng==", + "dev": true, + "license": "MIT" + }, + "node_modules/jose": { + "version": "6.2.10", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.10.tgz", + "integrity": "sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/karma": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", + "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.7.2", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", + "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-chrome-launcher/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/karma-coverage": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", + "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma-coverage/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/karma-jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", + "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jasmine-core": "^4.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "karma": "^6.0.0" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", + "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "jasmine-core": "^4.0.0 || ^5.0.0", + "karma": "^6.0.0", + "karma-jasmine": "^5.0.0" + } + }, + "node_modules/karma-jasmine/node_modules/jasmine-core": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.1.tgz", + "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/karma/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/karma/node_modules/body-parser": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/karma/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/karma/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/karma/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/karma/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/karma/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/karma/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/karma/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/karma/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/karma/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/karma/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/karma/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz", + "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/listr2": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.1.tgz", + "integrity": "sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lmdb": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.4.2.tgz", + "integrity": "sha512-nwVGUfTBUwJKXd6lRV8pFNfnrCC1+l49ESJRM19t/tFb/97QfJEixe5DYRvug5JO7DSFKoKaVy7oGMt5rVqZvg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "msgpackr": "^1.11.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.5.3", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.4.2", + "@lmdb/lmdb-darwin-x64": "3.4.2", + "@lmdb/lmdb-linux-arm": "3.4.2", + "@lmdb/lmdb-linux-arm64": "3.4.2", + "@lmdb/lmdb-linux-x64": "3.4.2", + "@lmdb/lmdb-win32-arm64": "3.4.2", + "@lmdb/lmdb-win32-x64": "3.4.2" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-fetch-happen": { + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.6.tgz", + "integrity": "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/agent": "^4.0.0", + "@npmcli/redact": "^4.0.0", + "cacache": "^20.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/make-fetch-happen/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz", + "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^2.0.0", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + }, + "optionalDependencies": { + "iconv-lite": "^0.7.2" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", + "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-sized": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", + "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.12.1.tgz", + "integrity": "sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==", + "dev": true, + "license": "MIT", + "optional": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-gyp": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", + "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz", + "integrity": "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^5.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-install-checks": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", + "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", + "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-package-arg": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.0.tgz", + "integrity": "sha512-+t2etZAGcB7TbbLHfDwooV9ppB2LhhcT6A+L9cahsf9mEUAoQ6CktLEVvEnpD0N5CkX7zJqnPGaFtoQDy9EkHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^9.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-packlist": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.4.tgz", + "integrity": "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==", + "dev": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^8.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-packlist/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.1.tgz", + "integrity": "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^4.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^15.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^13.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ordered-binary": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.1.tgz", + "integrity": "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/p-map": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.7.tgz", + "integrity": "sha512-VaWRu2i4FJNRtiRWCuuQRgfQ1B7a6+gMSrO+3j0EQi/k0ULfS9kosRxGoiqwzIjZTDI02tGfk5mXXltLg6QtfQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pacote": { + "version": "21.5.1", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.5.1.tgz", + "integrity": "sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/git": "^7.0.0", + "@npmcli/installed-package-contents": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "proc-log": "^6.0.0", + "sigstore": "^4.0.0", + "ssri": "^13.0.0", + "tar": "^7.4.3" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/pacote/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-8.0.0.tgz", + "integrity": "sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0", + "parse5": "^8.0.0", + "parse5-sax-parser": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-8.0.0.tgz", + "integrity": "sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/piscina": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.2.0.tgz", + "integrity": "sha512-DszUCKeVN/5G5QKo6jAVHL8fmKnkJvQ0ACiVgY7YGCq3TUB2oznAOayvZPIAdEThvhczkXR+qm3IHsNXpFCYfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.x" + }, + "optionalDependencies": { + "@napi-rs/nice": "^1.0.4" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true, + "license": "MIT" + }, + "node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.90.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz", + "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.1.tgz", + "integrity": "sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.1", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.1.1", + "@sigstore/tuf": "^4.0.2", + "@sigstore/verify": "^3.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz", + "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "~4.4.1", + "ws": "~8.21.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz", + "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/ssri": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", + "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tuf-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", + "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "4.1.0", + "debug": "^4.4.3", + "make-fetch-happen": "^15.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.41", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz", + "integrity": "sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/undici": { + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz", + "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", + "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, + "node_modules/zone.js": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz", + "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==", + "license": "MIT" + } + } +} diff --git a/src/frontend/package.json b/src/frontend/package.json new file mode 100644 index 0000000..a91946e --- /dev/null +++ b/src/frontend/package.json @@ -0,0 +1,48 @@ +{ + "name": "portfolio-frontend", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "prettier": { + "printWidth": 100, + "singleQuote": true, + "overrides": [ + { + "files": "*.html", + "options": { + "parser": "angular" + } + } + ] + }, + "private": true, + "dependencies": { + "@angular/common": "^20.3.0", + "@angular/compiler": "^20.3.0", + "@angular/core": "^20.3.0", + "@angular/forms": "^20.3.0", + "@angular/platform-browser": "^20.3.0", + "@angular/router": "^20.3.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.15.0" + }, + "devDependencies": { + "@angular/build": "^20.3.26", + "@angular/cli": "^20.3.26", + "@angular/compiler-cli": "^20.3.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.9.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.9.2" + } +} \ No newline at end of file diff --git a/src/frontend/public/assets/images/thinkpad-sequence/001.jpg b/src/frontend/public/assets/images/thinkpad-sequence/001.jpg new file mode 100644 index 0000000..1fe4396 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/001.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/002.jpg b/src/frontend/public/assets/images/thinkpad-sequence/002.jpg new file mode 100644 index 0000000..b5a3adc Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/002.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/003.jpg b/src/frontend/public/assets/images/thinkpad-sequence/003.jpg new file mode 100644 index 0000000..5790666 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/003.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/004.jpg b/src/frontend/public/assets/images/thinkpad-sequence/004.jpg new file mode 100644 index 0000000..1ae2f04 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/004.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/005.jpg b/src/frontend/public/assets/images/thinkpad-sequence/005.jpg new file mode 100644 index 0000000..d3145ad Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/005.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/006.jpg b/src/frontend/public/assets/images/thinkpad-sequence/006.jpg new file mode 100644 index 0000000..1e3dec2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/006.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/007.jpg b/src/frontend/public/assets/images/thinkpad-sequence/007.jpg new file mode 100644 index 0000000..10e4fb2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/007.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/008.jpg b/src/frontend/public/assets/images/thinkpad-sequence/008.jpg new file mode 100644 index 0000000..4dac970 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/008.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/009.jpg b/src/frontend/public/assets/images/thinkpad-sequence/009.jpg new file mode 100644 index 0000000..8fd5dde Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/009.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/010.jpg b/src/frontend/public/assets/images/thinkpad-sequence/010.jpg new file mode 100644 index 0000000..edfbe17 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/010.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/011.jpg b/src/frontend/public/assets/images/thinkpad-sequence/011.jpg new file mode 100644 index 0000000..ef5ffd8 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/011.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/012.jpg b/src/frontend/public/assets/images/thinkpad-sequence/012.jpg new file mode 100644 index 0000000..684fc0b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/012.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/013.jpg b/src/frontend/public/assets/images/thinkpad-sequence/013.jpg new file mode 100644 index 0000000..684fc0b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/013.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/014.jpg b/src/frontend/public/assets/images/thinkpad-sequence/014.jpg new file mode 100644 index 0000000..b3124b3 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/014.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/015.jpg b/src/frontend/public/assets/images/thinkpad-sequence/015.jpg new file mode 100644 index 0000000..d04ebd4 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/015.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/016.jpg b/src/frontend/public/assets/images/thinkpad-sequence/016.jpg new file mode 100644 index 0000000..27c9b01 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/016.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/017.jpg b/src/frontend/public/assets/images/thinkpad-sequence/017.jpg new file mode 100644 index 0000000..0a1c169 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/017.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/018.jpg b/src/frontend/public/assets/images/thinkpad-sequence/018.jpg new file mode 100644 index 0000000..0a1c169 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/018.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/019.jpg b/src/frontend/public/assets/images/thinkpad-sequence/019.jpg new file mode 100644 index 0000000..5d2706a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/019.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/020.jpg b/src/frontend/public/assets/images/thinkpad-sequence/020.jpg new file mode 100644 index 0000000..df10f4c Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/020.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/021.jpg b/src/frontend/public/assets/images/thinkpad-sequence/021.jpg new file mode 100644 index 0000000..3383170 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/021.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/022.jpg b/src/frontend/public/assets/images/thinkpad-sequence/022.jpg new file mode 100644 index 0000000..6621b64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/022.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/023.jpg b/src/frontend/public/assets/images/thinkpad-sequence/023.jpg new file mode 100644 index 0000000..6621b64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/023.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/024.jpg b/src/frontend/public/assets/images/thinkpad-sequence/024.jpg new file mode 100644 index 0000000..f6eab28 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/024.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/025.jpg b/src/frontend/public/assets/images/thinkpad-sequence/025.jpg new file mode 100644 index 0000000..fce313d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/025.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/026.jpg b/src/frontend/public/assets/images/thinkpad-sequence/026.jpg new file mode 100644 index 0000000..35537fb Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/026.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/027.jpg b/src/frontend/public/assets/images/thinkpad-sequence/027.jpg new file mode 100644 index 0000000..0ebff64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/027.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/028.jpg b/src/frontend/public/assets/images/thinkpad-sequence/028.jpg new file mode 100644 index 0000000..0ebff64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/028.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/029.jpg b/src/frontend/public/assets/images/thinkpad-sequence/029.jpg new file mode 100644 index 0000000..c0d4a8a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/029.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/030.jpg b/src/frontend/public/assets/images/thinkpad-sequence/030.jpg new file mode 100644 index 0000000..68c5ff5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/030.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/031.jpg b/src/frontend/public/assets/images/thinkpad-sequence/031.jpg new file mode 100644 index 0000000..bf15149 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/031.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/032.jpg b/src/frontend/public/assets/images/thinkpad-sequence/032.jpg new file mode 100644 index 0000000..995f54b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/032.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/033.jpg b/src/frontend/public/assets/images/thinkpad-sequence/033.jpg new file mode 100644 index 0000000..995f54b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/033.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/034.jpg b/src/frontend/public/assets/images/thinkpad-sequence/034.jpg new file mode 100644 index 0000000..2225431 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/034.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/035.jpg b/src/frontend/public/assets/images/thinkpad-sequence/035.jpg new file mode 100644 index 0000000..312a2ef Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/035.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/036.jpg b/src/frontend/public/assets/images/thinkpad-sequence/036.jpg new file mode 100644 index 0000000..9fc44a1 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/036.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/037.jpg b/src/frontend/public/assets/images/thinkpad-sequence/037.jpg new file mode 100644 index 0000000..22367cf Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/037.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/038.jpg b/src/frontend/public/assets/images/thinkpad-sequence/038.jpg new file mode 100644 index 0000000..22367cf Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/038.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/039.jpg b/src/frontend/public/assets/images/thinkpad-sequence/039.jpg new file mode 100644 index 0000000..1cba700 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/039.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/040.jpg b/src/frontend/public/assets/images/thinkpad-sequence/040.jpg new file mode 100644 index 0000000..e4ec8d7 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/040.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/041.jpg b/src/frontend/public/assets/images/thinkpad-sequence/041.jpg new file mode 100644 index 0000000..7f75495 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/041.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/042.jpg b/src/frontend/public/assets/images/thinkpad-sequence/042.jpg new file mode 100644 index 0000000..0a562e5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/042.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/043.jpg b/src/frontend/public/assets/images/thinkpad-sequence/043.jpg new file mode 100644 index 0000000..0a562e5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/043.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/044.jpg b/src/frontend/public/assets/images/thinkpad-sequence/044.jpg new file mode 100644 index 0000000..8f1f719 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/044.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/045.jpg b/src/frontend/public/assets/images/thinkpad-sequence/045.jpg new file mode 100644 index 0000000..924aa2f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/045.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/046.jpg b/src/frontend/public/assets/images/thinkpad-sequence/046.jpg new file mode 100644 index 0000000..95e0255 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/046.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/047.jpg b/src/frontend/public/assets/images/thinkpad-sequence/047.jpg new file mode 100644 index 0000000..ca0600e Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/047.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/048.jpg b/src/frontend/public/assets/images/thinkpad-sequence/048.jpg new file mode 100644 index 0000000..ca0600e Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/048.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/049.jpg b/src/frontend/public/assets/images/thinkpad-sequence/049.jpg new file mode 100644 index 0000000..eb403b7 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/049.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/050.jpg b/src/frontend/public/assets/images/thinkpad-sequence/050.jpg new file mode 100644 index 0000000..3c7bb5b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/050.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/051.jpg b/src/frontend/public/assets/images/thinkpad-sequence/051.jpg new file mode 100644 index 0000000..bdb5153 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/051.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/052.jpg b/src/frontend/public/assets/images/thinkpad-sequence/052.jpg new file mode 100644 index 0000000..b668b38 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/052.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/053.jpg b/src/frontend/public/assets/images/thinkpad-sequence/053.jpg new file mode 100644 index 0000000..b668b38 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/053.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/054.jpg b/src/frontend/public/assets/images/thinkpad-sequence/054.jpg new file mode 100644 index 0000000..f452835 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/054.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/055.jpg b/src/frontend/public/assets/images/thinkpad-sequence/055.jpg new file mode 100644 index 0000000..3adacc3 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/055.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/056.jpg b/src/frontend/public/assets/images/thinkpad-sequence/056.jpg new file mode 100644 index 0000000..cbe44d1 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/056.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/057.jpg b/src/frontend/public/assets/images/thinkpad-sequence/057.jpg new file mode 100644 index 0000000..7113d26 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/057.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/058.jpg b/src/frontend/public/assets/images/thinkpad-sequence/058.jpg new file mode 100644 index 0000000..7113d26 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/058.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/059.jpg b/src/frontend/public/assets/images/thinkpad-sequence/059.jpg new file mode 100644 index 0000000..e07cf5f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/059.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/060.jpg b/src/frontend/public/assets/images/thinkpad-sequence/060.jpg new file mode 100644 index 0000000..81f2d30 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/060.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/061.jpg b/src/frontend/public/assets/images/thinkpad-sequence/061.jpg new file mode 100644 index 0000000..b362b30 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/061.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/062.jpg b/src/frontend/public/assets/images/thinkpad-sequence/062.jpg new file mode 100644 index 0000000..235062c Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/062.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/063.jpg b/src/frontend/public/assets/images/thinkpad-sequence/063.jpg new file mode 100644 index 0000000..235062c Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/063.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/064.jpg b/src/frontend/public/assets/images/thinkpad-sequence/064.jpg new file mode 100644 index 0000000..b9f6d6a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/064.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/065.jpg b/src/frontend/public/assets/images/thinkpad-sequence/065.jpg new file mode 100644 index 0000000..91169b8 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/065.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/066.jpg b/src/frontend/public/assets/images/thinkpad-sequence/066.jpg new file mode 100644 index 0000000..0e4ab63 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/066.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/067.jpg b/src/frontend/public/assets/images/thinkpad-sequence/067.jpg new file mode 100644 index 0000000..93b5560 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/067.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/068.jpg b/src/frontend/public/assets/images/thinkpad-sequence/068.jpg new file mode 100644 index 0000000..93b5560 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/068.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/069.jpg b/src/frontend/public/assets/images/thinkpad-sequence/069.jpg new file mode 100644 index 0000000..ba98a31 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/069.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/070.jpg b/src/frontend/public/assets/images/thinkpad-sequence/070.jpg new file mode 100644 index 0000000..34c008d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/070.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/071.jpg b/src/frontend/public/assets/images/thinkpad-sequence/071.jpg new file mode 100644 index 0000000..84ad54a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/071.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/072.jpg b/src/frontend/public/assets/images/thinkpad-sequence/072.jpg new file mode 100644 index 0000000..25bd2ed Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/072.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/073.jpg b/src/frontend/public/assets/images/thinkpad-sequence/073.jpg new file mode 100644 index 0000000..25bd2ed Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/073.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/074.jpg b/src/frontend/public/assets/images/thinkpad-sequence/074.jpg new file mode 100644 index 0000000..9d49580 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/074.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/075.jpg b/src/frontend/public/assets/images/thinkpad-sequence/075.jpg new file mode 100644 index 0000000..314ab33 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/075.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/076.jpg b/src/frontend/public/assets/images/thinkpad-sequence/076.jpg new file mode 100644 index 0000000..fcba31d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/076.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/077.jpg b/src/frontend/public/assets/images/thinkpad-sequence/077.jpg new file mode 100644 index 0000000..382e810 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/077.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/078.jpg b/src/frontend/public/assets/images/thinkpad-sequence/078.jpg new file mode 100644 index 0000000..382e810 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/078.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/079.jpg b/src/frontend/public/assets/images/thinkpad-sequence/079.jpg new file mode 100644 index 0000000..c47b681 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/079.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/080.jpg b/src/frontend/public/assets/images/thinkpad-sequence/080.jpg new file mode 100644 index 0000000..9eb5cee Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/080.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/081.jpg b/src/frontend/public/assets/images/thinkpad-sequence/081.jpg new file mode 100644 index 0000000..a4a174b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/081.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/082.jpg b/src/frontend/public/assets/images/thinkpad-sequence/082.jpg new file mode 100644 index 0000000..b913306 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/082.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/083.jpg b/src/frontend/public/assets/images/thinkpad-sequence/083.jpg new file mode 100644 index 0000000..b913306 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/083.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/084.jpg b/src/frontend/public/assets/images/thinkpad-sequence/084.jpg new file mode 100644 index 0000000..aa052d1 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/084.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/085.jpg b/src/frontend/public/assets/images/thinkpad-sequence/085.jpg new file mode 100644 index 0000000..5250d82 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/085.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/086.jpg b/src/frontend/public/assets/images/thinkpad-sequence/086.jpg new file mode 100644 index 0000000..024fb08 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/086.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/087.jpg b/src/frontend/public/assets/images/thinkpad-sequence/087.jpg new file mode 100644 index 0000000..2486eda Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/087.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/088.jpg b/src/frontend/public/assets/images/thinkpad-sequence/088.jpg new file mode 100644 index 0000000..2486eda Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/088.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/089.jpg b/src/frontend/public/assets/images/thinkpad-sequence/089.jpg new file mode 100644 index 0000000..288d4dd Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/089.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/090.jpg b/src/frontend/public/assets/images/thinkpad-sequence/090.jpg new file mode 100644 index 0000000..a5250f4 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/090.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/091.jpg b/src/frontend/public/assets/images/thinkpad-sequence/091.jpg new file mode 100644 index 0000000..a2ff547 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/091.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/092.jpg b/src/frontend/public/assets/images/thinkpad-sequence/092.jpg new file mode 100644 index 0000000..bb74a94 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/092.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/093.jpg b/src/frontend/public/assets/images/thinkpad-sequence/093.jpg new file mode 100644 index 0000000..bb74a94 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/093.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/094.jpg b/src/frontend/public/assets/images/thinkpad-sequence/094.jpg new file mode 100644 index 0000000..5e5afed Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/094.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/095.jpg b/src/frontend/public/assets/images/thinkpad-sequence/095.jpg new file mode 100644 index 0000000..8370d9d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/095.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/096.jpg b/src/frontend/public/assets/images/thinkpad-sequence/096.jpg new file mode 100644 index 0000000..83550a6 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/096.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/097.jpg b/src/frontend/public/assets/images/thinkpad-sequence/097.jpg new file mode 100644 index 0000000..12cb9fb Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/097.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/098.jpg b/src/frontend/public/assets/images/thinkpad-sequence/098.jpg new file mode 100644 index 0000000..12cb9fb Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/098.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/099.jpg b/src/frontend/public/assets/images/thinkpad-sequence/099.jpg new file mode 100644 index 0000000..291c296 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/099.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/100.jpg b/src/frontend/public/assets/images/thinkpad-sequence/100.jpg new file mode 100644 index 0000000..5171466 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/100.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/101.jpg b/src/frontend/public/assets/images/thinkpad-sequence/101.jpg new file mode 100644 index 0000000..ef2189d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/101.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/102.jpg b/src/frontend/public/assets/images/thinkpad-sequence/102.jpg new file mode 100644 index 0000000..3fb70d9 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/102.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/103.jpg b/src/frontend/public/assets/images/thinkpad-sequence/103.jpg new file mode 100644 index 0000000..3fb70d9 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/103.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/104.jpg b/src/frontend/public/assets/images/thinkpad-sequence/104.jpg new file mode 100644 index 0000000..066a111 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/104.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/105.jpg b/src/frontend/public/assets/images/thinkpad-sequence/105.jpg new file mode 100644 index 0000000..8c6c7a3 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/105.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/106.jpg b/src/frontend/public/assets/images/thinkpad-sequence/106.jpg new file mode 100644 index 0000000..b88ff99 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/106.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/107.jpg b/src/frontend/public/assets/images/thinkpad-sequence/107.jpg new file mode 100644 index 0000000..a56877f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/107.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/108.jpg b/src/frontend/public/assets/images/thinkpad-sequence/108.jpg new file mode 100644 index 0000000..a56877f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/108.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/109.jpg b/src/frontend/public/assets/images/thinkpad-sequence/109.jpg new file mode 100644 index 0000000..ec4be1e Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/109.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/110.jpg b/src/frontend/public/assets/images/thinkpad-sequence/110.jpg new file mode 100644 index 0000000..57b7b73 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/110.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/111.jpg b/src/frontend/public/assets/images/thinkpad-sequence/111.jpg new file mode 100644 index 0000000..d042c01 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/111.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/112.jpg b/src/frontend/public/assets/images/thinkpad-sequence/112.jpg new file mode 100644 index 0000000..3948e70 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/112.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/113.jpg b/src/frontend/public/assets/images/thinkpad-sequence/113.jpg new file mode 100644 index 0000000..3948e70 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/113.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/114.jpg b/src/frontend/public/assets/images/thinkpad-sequence/114.jpg new file mode 100644 index 0000000..c59a715 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/114.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/115.jpg b/src/frontend/public/assets/images/thinkpad-sequence/115.jpg new file mode 100644 index 0000000..1069cda Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/115.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/116.jpg b/src/frontend/public/assets/images/thinkpad-sequence/116.jpg new file mode 100644 index 0000000..1f8a03f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/116.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/117.jpg b/src/frontend/public/assets/images/thinkpad-sequence/117.jpg new file mode 100644 index 0000000..eb5a291 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/117.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/118.jpg b/src/frontend/public/assets/images/thinkpad-sequence/118.jpg new file mode 100644 index 0000000..eb5a291 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/118.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/119.jpg b/src/frontend/public/assets/images/thinkpad-sequence/119.jpg new file mode 100644 index 0000000..6501b46 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/119.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/120.jpg b/src/frontend/public/assets/images/thinkpad-sequence/120.jpg new file mode 100644 index 0000000..90ef155 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/120.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/121.jpg b/src/frontend/public/assets/images/thinkpad-sequence/121.jpg new file mode 100644 index 0000000..bfc1287 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/121.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/122.jpg b/src/frontend/public/assets/images/thinkpad-sequence/122.jpg new file mode 100644 index 0000000..5cab0d0 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/122.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/123.jpg b/src/frontend/public/assets/images/thinkpad-sequence/123.jpg new file mode 100644 index 0000000..5cab0d0 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/123.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/124.jpg b/src/frontend/public/assets/images/thinkpad-sequence/124.jpg new file mode 100644 index 0000000..374e10b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/124.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/125.jpg b/src/frontend/public/assets/images/thinkpad-sequence/125.jpg new file mode 100644 index 0000000..5fac1d5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/125.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/126.jpg b/src/frontend/public/assets/images/thinkpad-sequence/126.jpg new file mode 100644 index 0000000..87b8d22 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/126.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/127.jpg b/src/frontend/public/assets/images/thinkpad-sequence/127.jpg new file mode 100644 index 0000000..bfa933a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/127.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/128.jpg b/src/frontend/public/assets/images/thinkpad-sequence/128.jpg new file mode 100644 index 0000000..bfa933a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/128.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/129.jpg b/src/frontend/public/assets/images/thinkpad-sequence/129.jpg new file mode 100644 index 0000000..606da15 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/129.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/130.jpg b/src/frontend/public/assets/images/thinkpad-sequence/130.jpg new file mode 100644 index 0000000..7be502b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/130.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/131.jpg b/src/frontend/public/assets/images/thinkpad-sequence/131.jpg new file mode 100644 index 0000000..c8df466 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/131.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/132.jpg b/src/frontend/public/assets/images/thinkpad-sequence/132.jpg new file mode 100644 index 0000000..eecb611 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/132.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/133.jpg b/src/frontend/public/assets/images/thinkpad-sequence/133.jpg new file mode 100644 index 0000000..eecb611 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/133.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/134.jpg b/src/frontend/public/assets/images/thinkpad-sequence/134.jpg new file mode 100644 index 0000000..f2dad47 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/134.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/135.jpg b/src/frontend/public/assets/images/thinkpad-sequence/135.jpg new file mode 100644 index 0000000..85697ab Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/135.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/136.jpg b/src/frontend/public/assets/images/thinkpad-sequence/136.jpg new file mode 100644 index 0000000..935fedd Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/136.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/137.jpg b/src/frontend/public/assets/images/thinkpad-sequence/137.jpg new file mode 100644 index 0000000..8b61be2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/137.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/138.jpg b/src/frontend/public/assets/images/thinkpad-sequence/138.jpg new file mode 100644 index 0000000..8b61be2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/138.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/139.jpg b/src/frontend/public/assets/images/thinkpad-sequence/139.jpg new file mode 100644 index 0000000..85ed447 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/139.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/140.jpg b/src/frontend/public/assets/images/thinkpad-sequence/140.jpg new file mode 100644 index 0000000..4c672e0 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/140.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/141.jpg b/src/frontend/public/assets/images/thinkpad-sequence/141.jpg new file mode 100644 index 0000000..a29f1a9 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/141.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/142.jpg b/src/frontend/public/assets/images/thinkpad-sequence/142.jpg new file mode 100644 index 0000000..1298849 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/142.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/143.jpg b/src/frontend/public/assets/images/thinkpad-sequence/143.jpg new file mode 100644 index 0000000..1298849 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/143.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/144.jpg b/src/frontend/public/assets/images/thinkpad-sequence/144.jpg new file mode 100644 index 0000000..c478a9d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/144.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/145.jpg b/src/frontend/public/assets/images/thinkpad-sequence/145.jpg new file mode 100644 index 0000000..218426a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/145.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/146.jpg b/src/frontend/public/assets/images/thinkpad-sequence/146.jpg new file mode 100644 index 0000000..37b0650 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/146.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/147.jpg b/src/frontend/public/assets/images/thinkpad-sequence/147.jpg new file mode 100644 index 0000000..0b00b64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/147.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/148.jpg b/src/frontend/public/assets/images/thinkpad-sequence/148.jpg new file mode 100644 index 0000000..0b00b64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/148.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/149.jpg b/src/frontend/public/assets/images/thinkpad-sequence/149.jpg new file mode 100644 index 0000000..88347df Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/149.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/150.jpg b/src/frontend/public/assets/images/thinkpad-sequence/150.jpg new file mode 100644 index 0000000..632b15f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/150.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/151.jpg b/src/frontend/public/assets/images/thinkpad-sequence/151.jpg new file mode 100644 index 0000000..46119b8 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/151.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/152.jpg b/src/frontend/public/assets/images/thinkpad-sequence/152.jpg new file mode 100644 index 0000000..97f6354 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/152.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/153.jpg b/src/frontend/public/assets/images/thinkpad-sequence/153.jpg new file mode 100644 index 0000000..97f6354 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/153.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/154.jpg b/src/frontend/public/assets/images/thinkpad-sequence/154.jpg new file mode 100644 index 0000000..ed51a36 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/154.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/155.jpg b/src/frontend/public/assets/images/thinkpad-sequence/155.jpg new file mode 100644 index 0000000..8f92486 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/155.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/156.jpg b/src/frontend/public/assets/images/thinkpad-sequence/156.jpg new file mode 100644 index 0000000..8e5bb37 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/156.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/157.jpg b/src/frontend/public/assets/images/thinkpad-sequence/157.jpg new file mode 100644 index 0000000..72a15ef Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/157.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/158.jpg b/src/frontend/public/assets/images/thinkpad-sequence/158.jpg new file mode 100644 index 0000000..72a15ef Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/158.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/159.jpg b/src/frontend/public/assets/images/thinkpad-sequence/159.jpg new file mode 100644 index 0000000..2fd18dc Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/159.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/160.jpg b/src/frontend/public/assets/images/thinkpad-sequence/160.jpg new file mode 100644 index 0000000..4b0471f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/160.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/161.jpg b/src/frontend/public/assets/images/thinkpad-sequence/161.jpg new file mode 100644 index 0000000..0e25332 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/161.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/162.jpg b/src/frontend/public/assets/images/thinkpad-sequence/162.jpg new file mode 100644 index 0000000..5e26fc2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/162.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/163.jpg b/src/frontend/public/assets/images/thinkpad-sequence/163.jpg new file mode 100644 index 0000000..5e26fc2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/163.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/164.jpg b/src/frontend/public/assets/images/thinkpad-sequence/164.jpg new file mode 100644 index 0000000..8d69690 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/164.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/165.jpg b/src/frontend/public/assets/images/thinkpad-sequence/165.jpg new file mode 100644 index 0000000..13574dd Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/165.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/166.jpg b/src/frontend/public/assets/images/thinkpad-sequence/166.jpg new file mode 100644 index 0000000..a961820 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/166.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/167.jpg b/src/frontend/public/assets/images/thinkpad-sequence/167.jpg new file mode 100644 index 0000000..58344ad Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/167.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/168.jpg b/src/frontend/public/assets/images/thinkpad-sequence/168.jpg new file mode 100644 index 0000000..58344ad Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/168.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/169.jpg b/src/frontend/public/assets/images/thinkpad-sequence/169.jpg new file mode 100644 index 0000000..a64a3a9 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/169.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/170.jpg b/src/frontend/public/assets/images/thinkpad-sequence/170.jpg new file mode 100644 index 0000000..2e6a520 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/170.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/171.jpg b/src/frontend/public/assets/images/thinkpad-sequence/171.jpg new file mode 100644 index 0000000..90aed8b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/171.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/172.jpg b/src/frontend/public/assets/images/thinkpad-sequence/172.jpg new file mode 100644 index 0000000..8563cba Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/172.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/173.jpg b/src/frontend/public/assets/images/thinkpad-sequence/173.jpg new file mode 100644 index 0000000..8563cba Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/173.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/174.jpg b/src/frontend/public/assets/images/thinkpad-sequence/174.jpg new file mode 100644 index 0000000..d72651f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/174.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/175.jpg b/src/frontend/public/assets/images/thinkpad-sequence/175.jpg new file mode 100644 index 0000000..878ae22 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/175.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/176.jpg b/src/frontend/public/assets/images/thinkpad-sequence/176.jpg new file mode 100644 index 0000000..9b10379 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/176.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/177.jpg b/src/frontend/public/assets/images/thinkpad-sequence/177.jpg new file mode 100644 index 0000000..fa8dc32 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/177.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/178.jpg b/src/frontend/public/assets/images/thinkpad-sequence/178.jpg new file mode 100644 index 0000000..fa8dc32 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/178.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/179.jpg b/src/frontend/public/assets/images/thinkpad-sequence/179.jpg new file mode 100644 index 0000000..fc2d563 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/179.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/180.jpg b/src/frontend/public/assets/images/thinkpad-sequence/180.jpg new file mode 100644 index 0000000..1c12541 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/180.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/181.jpg b/src/frontend/public/assets/images/thinkpad-sequence/181.jpg new file mode 100644 index 0000000..4d3cb8b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/181.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/182.jpg b/src/frontend/public/assets/images/thinkpad-sequence/182.jpg new file mode 100644 index 0000000..ee16f61 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/182.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/183.jpg b/src/frontend/public/assets/images/thinkpad-sequence/183.jpg new file mode 100644 index 0000000..ee16f61 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/183.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/184.jpg b/src/frontend/public/assets/images/thinkpad-sequence/184.jpg new file mode 100644 index 0000000..a6d6899 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/184.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/185.jpg b/src/frontend/public/assets/images/thinkpad-sequence/185.jpg new file mode 100644 index 0000000..f6b984d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/185.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/186.jpg b/src/frontend/public/assets/images/thinkpad-sequence/186.jpg new file mode 100644 index 0000000..0620171 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/186.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/187.jpg b/src/frontend/public/assets/images/thinkpad-sequence/187.jpg new file mode 100644 index 0000000..b49343d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/187.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/188.jpg b/src/frontend/public/assets/images/thinkpad-sequence/188.jpg new file mode 100644 index 0000000..b49343d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/188.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/189.jpg b/src/frontend/public/assets/images/thinkpad-sequence/189.jpg new file mode 100644 index 0000000..908ce09 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/189.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/190.jpg b/src/frontend/public/assets/images/thinkpad-sequence/190.jpg new file mode 100644 index 0000000..24d71c6 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/190.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/191.jpg b/src/frontend/public/assets/images/thinkpad-sequence/191.jpg new file mode 100644 index 0000000..ce5b97f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/191.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/192.jpg b/src/frontend/public/assets/images/thinkpad-sequence/192.jpg new file mode 100644 index 0000000..2f255be Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/192.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/193.jpg b/src/frontend/public/assets/images/thinkpad-sequence/193.jpg new file mode 100644 index 0000000..2f255be Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/193.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/194.jpg b/src/frontend/public/assets/images/thinkpad-sequence/194.jpg new file mode 100644 index 0000000..964a3a3 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/194.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/195.jpg b/src/frontend/public/assets/images/thinkpad-sequence/195.jpg new file mode 100644 index 0000000..6dd280a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/195.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/196.jpg b/src/frontend/public/assets/images/thinkpad-sequence/196.jpg new file mode 100644 index 0000000..50c0744 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/196.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/197.jpg b/src/frontend/public/assets/images/thinkpad-sequence/197.jpg new file mode 100644 index 0000000..a1ab43d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/197.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/198.jpg b/src/frontend/public/assets/images/thinkpad-sequence/198.jpg new file mode 100644 index 0000000..a1ab43d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/198.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/199.jpg b/src/frontend/public/assets/images/thinkpad-sequence/199.jpg new file mode 100644 index 0000000..8296e66 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/199.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/200.jpg b/src/frontend/public/assets/images/thinkpad-sequence/200.jpg new file mode 100644 index 0000000..3b16784 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/200.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/201.jpg b/src/frontend/public/assets/images/thinkpad-sequence/201.jpg new file mode 100644 index 0000000..f259b43 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/201.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/202.jpg b/src/frontend/public/assets/images/thinkpad-sequence/202.jpg new file mode 100644 index 0000000..ad17a01 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/202.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/203.jpg b/src/frontend/public/assets/images/thinkpad-sequence/203.jpg new file mode 100644 index 0000000..ad17a01 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/203.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/204.jpg b/src/frontend/public/assets/images/thinkpad-sequence/204.jpg new file mode 100644 index 0000000..c452c9a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/204.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/205.jpg b/src/frontend/public/assets/images/thinkpad-sequence/205.jpg new file mode 100644 index 0000000..a83baf7 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/205.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/206.jpg b/src/frontend/public/assets/images/thinkpad-sequence/206.jpg new file mode 100644 index 0000000..fe62206 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/206.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/207.jpg b/src/frontend/public/assets/images/thinkpad-sequence/207.jpg new file mode 100644 index 0000000..be10dd0 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/207.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/208.jpg b/src/frontend/public/assets/images/thinkpad-sequence/208.jpg new file mode 100644 index 0000000..be10dd0 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/208.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/209.jpg b/src/frontend/public/assets/images/thinkpad-sequence/209.jpg new file mode 100644 index 0000000..e2b8219 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/209.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/210.jpg b/src/frontend/public/assets/images/thinkpad-sequence/210.jpg new file mode 100644 index 0000000..c90ef9d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/210.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/211.jpg b/src/frontend/public/assets/images/thinkpad-sequence/211.jpg new file mode 100644 index 0000000..b6ddb57 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/211.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/212.jpg b/src/frontend/public/assets/images/thinkpad-sequence/212.jpg new file mode 100644 index 0000000..30ba939 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/212.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/213.jpg b/src/frontend/public/assets/images/thinkpad-sequence/213.jpg new file mode 100644 index 0000000..30ba939 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/213.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/214.jpg b/src/frontend/public/assets/images/thinkpad-sequence/214.jpg new file mode 100644 index 0000000..b7f52f7 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/214.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/215.jpg b/src/frontend/public/assets/images/thinkpad-sequence/215.jpg new file mode 100644 index 0000000..d0f9974 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/215.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/216.jpg b/src/frontend/public/assets/images/thinkpad-sequence/216.jpg new file mode 100644 index 0000000..6c4c2dd Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/216.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/217.jpg b/src/frontend/public/assets/images/thinkpad-sequence/217.jpg new file mode 100644 index 0000000..31e685e Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/217.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/218.jpg b/src/frontend/public/assets/images/thinkpad-sequence/218.jpg new file mode 100644 index 0000000..31e685e Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/218.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/219.jpg b/src/frontend/public/assets/images/thinkpad-sequence/219.jpg new file mode 100644 index 0000000..915db56 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/219.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/220.jpg b/src/frontend/public/assets/images/thinkpad-sequence/220.jpg new file mode 100644 index 0000000..ae3ac04 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/220.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/221.jpg b/src/frontend/public/assets/images/thinkpad-sequence/221.jpg new file mode 100644 index 0000000..0de0f30 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/221.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/222.jpg b/src/frontend/public/assets/images/thinkpad-sequence/222.jpg new file mode 100644 index 0000000..59a7641 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/222.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/223.jpg b/src/frontend/public/assets/images/thinkpad-sequence/223.jpg new file mode 100644 index 0000000..59a7641 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/223.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/224.jpg b/src/frontend/public/assets/images/thinkpad-sequence/224.jpg new file mode 100644 index 0000000..1c18e80 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/224.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/225.jpg b/src/frontend/public/assets/images/thinkpad-sequence/225.jpg new file mode 100644 index 0000000..76b1d55 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/225.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/226.jpg b/src/frontend/public/assets/images/thinkpad-sequence/226.jpg new file mode 100644 index 0000000..e27d565 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/226.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/227.jpg b/src/frontend/public/assets/images/thinkpad-sequence/227.jpg new file mode 100644 index 0000000..af960f5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/227.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/228.jpg b/src/frontend/public/assets/images/thinkpad-sequence/228.jpg new file mode 100644 index 0000000..af960f5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/228.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/229.jpg b/src/frontend/public/assets/images/thinkpad-sequence/229.jpg new file mode 100644 index 0000000..889537a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/229.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/230.jpg b/src/frontend/public/assets/images/thinkpad-sequence/230.jpg new file mode 100644 index 0000000..2f46fd6 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/230.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/231.jpg b/src/frontend/public/assets/images/thinkpad-sequence/231.jpg new file mode 100644 index 0000000..c9d9026 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/231.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/232.jpg b/src/frontend/public/assets/images/thinkpad-sequence/232.jpg new file mode 100644 index 0000000..5142693 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/232.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/233.jpg b/src/frontend/public/assets/images/thinkpad-sequence/233.jpg new file mode 100644 index 0000000..5142693 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/233.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/234.jpg b/src/frontend/public/assets/images/thinkpad-sequence/234.jpg new file mode 100644 index 0000000..d46b26f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/234.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/235.jpg b/src/frontend/public/assets/images/thinkpad-sequence/235.jpg new file mode 100644 index 0000000..55a4a8b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/235.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/236.jpg b/src/frontend/public/assets/images/thinkpad-sequence/236.jpg new file mode 100644 index 0000000..050c352 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/236.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/237.jpg b/src/frontend/public/assets/images/thinkpad-sequence/237.jpg new file mode 100644 index 0000000..92980a1 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/237.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/238.jpg b/src/frontend/public/assets/images/thinkpad-sequence/238.jpg new file mode 100644 index 0000000..92980a1 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/238.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/239.jpg b/src/frontend/public/assets/images/thinkpad-sequence/239.jpg new file mode 100644 index 0000000..1ccc9ba Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/239.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/240.jpg b/src/frontend/public/assets/images/thinkpad-sequence/240.jpg new file mode 100644 index 0000000..e71e1cb Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/240.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-001.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-001.jpg new file mode 100644 index 0000000..1fe4396 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-001.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-002.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-002.jpg new file mode 100644 index 0000000..b5a3adc Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-002.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-003.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-003.jpg new file mode 100644 index 0000000..5790666 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-003.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-004.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-004.jpg new file mode 100644 index 0000000..1ae2f04 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-004.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-005.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-005.jpg new file mode 100644 index 0000000..d3145ad Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-005.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-006.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-006.jpg new file mode 100644 index 0000000..1e3dec2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-006.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-007.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-007.jpg new file mode 100644 index 0000000..10e4fb2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-007.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-008.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-008.jpg new file mode 100644 index 0000000..4dac970 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-008.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-009.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-009.jpg new file mode 100644 index 0000000..8fd5dde Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-009.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-010.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-010.jpg new file mode 100644 index 0000000..edfbe17 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-010.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-011.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-011.jpg new file mode 100644 index 0000000..ef5ffd8 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-011.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-012.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-012.jpg new file mode 100644 index 0000000..684fc0b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-012.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-013.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-013.jpg new file mode 100644 index 0000000..684fc0b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-013.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-014.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-014.jpg new file mode 100644 index 0000000..b3124b3 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-014.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-015.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-015.jpg new file mode 100644 index 0000000..d04ebd4 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-015.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-016.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-016.jpg new file mode 100644 index 0000000..27c9b01 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-016.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-017.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-017.jpg new file mode 100644 index 0000000..0a1c169 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-017.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-018.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-018.jpg new file mode 100644 index 0000000..0a1c169 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-018.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-019.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-019.jpg new file mode 100644 index 0000000..5d2706a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-019.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-020.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-020.jpg new file mode 100644 index 0000000..df10f4c Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-020.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-021.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-021.jpg new file mode 100644 index 0000000..3383170 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-021.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-022.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-022.jpg new file mode 100644 index 0000000..6621b64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-022.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-023.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-023.jpg new file mode 100644 index 0000000..6621b64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-023.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-024.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-024.jpg new file mode 100644 index 0000000..f6eab28 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-024.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-025.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-025.jpg new file mode 100644 index 0000000..fce313d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-025.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-026.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-026.jpg new file mode 100644 index 0000000..35537fb Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-026.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-027.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-027.jpg new file mode 100644 index 0000000..0ebff64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-027.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-028.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-028.jpg new file mode 100644 index 0000000..0ebff64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-028.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-029.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-029.jpg new file mode 100644 index 0000000..c0d4a8a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-029.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-030.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-030.jpg new file mode 100644 index 0000000..68c5ff5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-030.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-031.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-031.jpg new file mode 100644 index 0000000..bf15149 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-031.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-032.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-032.jpg new file mode 100644 index 0000000..995f54b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-032.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-033.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-033.jpg new file mode 100644 index 0000000..995f54b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-033.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-034.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-034.jpg new file mode 100644 index 0000000..2225431 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-034.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-035.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-035.jpg new file mode 100644 index 0000000..312a2ef Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-035.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-036.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-036.jpg new file mode 100644 index 0000000..9fc44a1 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-036.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-037.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-037.jpg new file mode 100644 index 0000000..22367cf Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-037.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-038.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-038.jpg new file mode 100644 index 0000000..22367cf Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-038.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-039.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-039.jpg new file mode 100644 index 0000000..1cba700 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-039.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-040.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-040.jpg new file mode 100644 index 0000000..e4ec8d7 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-040.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-041.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-041.jpg new file mode 100644 index 0000000..7f75495 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-041.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-042.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-042.jpg new file mode 100644 index 0000000..0a562e5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-042.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-043.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-043.jpg new file mode 100644 index 0000000..0a562e5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-043.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-044.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-044.jpg new file mode 100644 index 0000000..8f1f719 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-044.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-045.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-045.jpg new file mode 100644 index 0000000..924aa2f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-045.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-046.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-046.jpg new file mode 100644 index 0000000..95e0255 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-046.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-047.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-047.jpg new file mode 100644 index 0000000..ca0600e Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-047.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-048.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-048.jpg new file mode 100644 index 0000000..ca0600e Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-048.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-049.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-049.jpg new file mode 100644 index 0000000..eb403b7 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-049.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-050.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-050.jpg new file mode 100644 index 0000000..3c7bb5b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-050.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-051.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-051.jpg new file mode 100644 index 0000000..bdb5153 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-051.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-052.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-052.jpg new file mode 100644 index 0000000..b668b38 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-052.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-053.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-053.jpg new file mode 100644 index 0000000..b668b38 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-053.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-054.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-054.jpg new file mode 100644 index 0000000..f452835 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-054.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-055.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-055.jpg new file mode 100644 index 0000000..3adacc3 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-055.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-056.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-056.jpg new file mode 100644 index 0000000..cbe44d1 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-056.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-057.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-057.jpg new file mode 100644 index 0000000..7113d26 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-057.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-058.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-058.jpg new file mode 100644 index 0000000..7113d26 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-058.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-059.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-059.jpg new file mode 100644 index 0000000..e07cf5f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-059.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-060.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-060.jpg new file mode 100644 index 0000000..81f2d30 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-060.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-061.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-061.jpg new file mode 100644 index 0000000..b362b30 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-061.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-062.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-062.jpg new file mode 100644 index 0000000..235062c Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-062.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-063.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-063.jpg new file mode 100644 index 0000000..235062c Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-063.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-064.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-064.jpg new file mode 100644 index 0000000..b9f6d6a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-064.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-065.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-065.jpg new file mode 100644 index 0000000..91169b8 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-065.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-066.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-066.jpg new file mode 100644 index 0000000..0e4ab63 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-066.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-067.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-067.jpg new file mode 100644 index 0000000..93b5560 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-067.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-068.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-068.jpg new file mode 100644 index 0000000..93b5560 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-068.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-069.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-069.jpg new file mode 100644 index 0000000..ba98a31 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-069.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-070.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-070.jpg new file mode 100644 index 0000000..34c008d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-070.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-071.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-071.jpg new file mode 100644 index 0000000..84ad54a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-071.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-072.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-072.jpg new file mode 100644 index 0000000..25bd2ed Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-072.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-073.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-073.jpg new file mode 100644 index 0000000..25bd2ed Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-073.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-074.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-074.jpg new file mode 100644 index 0000000..9d49580 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-074.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-075.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-075.jpg new file mode 100644 index 0000000..314ab33 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-075.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-076.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-076.jpg new file mode 100644 index 0000000..fcba31d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-076.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-077.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-077.jpg new file mode 100644 index 0000000..382e810 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-077.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-078.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-078.jpg new file mode 100644 index 0000000..382e810 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-078.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-079.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-079.jpg new file mode 100644 index 0000000..c47b681 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-079.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-080.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-080.jpg new file mode 100644 index 0000000..9eb5cee Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-080.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-081.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-081.jpg new file mode 100644 index 0000000..a4a174b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-081.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-082.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-082.jpg new file mode 100644 index 0000000..b913306 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-082.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-083.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-083.jpg new file mode 100644 index 0000000..b913306 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-083.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-084.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-084.jpg new file mode 100644 index 0000000..aa052d1 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-084.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-085.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-085.jpg new file mode 100644 index 0000000..5250d82 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-085.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-086.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-086.jpg new file mode 100644 index 0000000..024fb08 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-086.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-087.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-087.jpg new file mode 100644 index 0000000..2486eda Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-087.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-088.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-088.jpg new file mode 100644 index 0000000..2486eda Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-088.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-089.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-089.jpg new file mode 100644 index 0000000..288d4dd Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-089.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-090.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-090.jpg new file mode 100644 index 0000000..a5250f4 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-090.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-091.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-091.jpg new file mode 100644 index 0000000..a2ff547 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-091.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-092.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-092.jpg new file mode 100644 index 0000000..bb74a94 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-092.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-093.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-093.jpg new file mode 100644 index 0000000..bb74a94 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-093.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-094.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-094.jpg new file mode 100644 index 0000000..5e5afed Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-094.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-095.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-095.jpg new file mode 100644 index 0000000..8370d9d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-095.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-096.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-096.jpg new file mode 100644 index 0000000..83550a6 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-096.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-097.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-097.jpg new file mode 100644 index 0000000..12cb9fb Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-097.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-098.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-098.jpg new file mode 100644 index 0000000..12cb9fb Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-098.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-099.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-099.jpg new file mode 100644 index 0000000..291c296 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-099.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-100.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-100.jpg new file mode 100644 index 0000000..5171466 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-100.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-101.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-101.jpg new file mode 100644 index 0000000..ef2189d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-101.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-102.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-102.jpg new file mode 100644 index 0000000..3fb70d9 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-102.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-103.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-103.jpg new file mode 100644 index 0000000..3fb70d9 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-103.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-104.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-104.jpg new file mode 100644 index 0000000..066a111 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-104.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-105.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-105.jpg new file mode 100644 index 0000000..8c6c7a3 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-105.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-106.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-106.jpg new file mode 100644 index 0000000..b88ff99 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-106.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-107.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-107.jpg new file mode 100644 index 0000000..a56877f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-107.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-108.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-108.jpg new file mode 100644 index 0000000..a56877f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-108.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-109.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-109.jpg new file mode 100644 index 0000000..ec4be1e Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-109.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-110.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-110.jpg new file mode 100644 index 0000000..57b7b73 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-110.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-111.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-111.jpg new file mode 100644 index 0000000..d042c01 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-111.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-112.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-112.jpg new file mode 100644 index 0000000..3948e70 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-112.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-113.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-113.jpg new file mode 100644 index 0000000..3948e70 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-113.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-114.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-114.jpg new file mode 100644 index 0000000..c59a715 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-114.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-115.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-115.jpg new file mode 100644 index 0000000..1069cda Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-115.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-116.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-116.jpg new file mode 100644 index 0000000..1f8a03f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-116.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-117.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-117.jpg new file mode 100644 index 0000000..eb5a291 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-117.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-118.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-118.jpg new file mode 100644 index 0000000..eb5a291 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-118.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-119.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-119.jpg new file mode 100644 index 0000000..6501b46 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-119.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-120.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-120.jpg new file mode 100644 index 0000000..90ef155 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-120.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-121.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-121.jpg new file mode 100644 index 0000000..bfc1287 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-121.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-122.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-122.jpg new file mode 100644 index 0000000..5cab0d0 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-122.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-123.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-123.jpg new file mode 100644 index 0000000..5cab0d0 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-123.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-124.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-124.jpg new file mode 100644 index 0000000..374e10b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-124.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-125.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-125.jpg new file mode 100644 index 0000000..5fac1d5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-125.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-126.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-126.jpg new file mode 100644 index 0000000..87b8d22 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-126.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-127.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-127.jpg new file mode 100644 index 0000000..bfa933a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-127.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-128.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-128.jpg new file mode 100644 index 0000000..bfa933a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-128.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-129.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-129.jpg new file mode 100644 index 0000000..606da15 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-129.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-130.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-130.jpg new file mode 100644 index 0000000..7be502b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-130.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-131.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-131.jpg new file mode 100644 index 0000000..c8df466 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-131.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-132.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-132.jpg new file mode 100644 index 0000000..eecb611 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-132.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-133.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-133.jpg new file mode 100644 index 0000000..eecb611 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-133.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-134.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-134.jpg new file mode 100644 index 0000000..f2dad47 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-134.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-135.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-135.jpg new file mode 100644 index 0000000..85697ab Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-135.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-136.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-136.jpg new file mode 100644 index 0000000..935fedd Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-136.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-137.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-137.jpg new file mode 100644 index 0000000..8b61be2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-137.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-138.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-138.jpg new file mode 100644 index 0000000..8b61be2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-138.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-139.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-139.jpg new file mode 100644 index 0000000..85ed447 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-139.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-140.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-140.jpg new file mode 100644 index 0000000..4c672e0 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-140.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-141.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-141.jpg new file mode 100644 index 0000000..a29f1a9 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-141.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-142.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-142.jpg new file mode 100644 index 0000000..1298849 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-142.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-143.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-143.jpg new file mode 100644 index 0000000..1298849 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-143.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-144.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-144.jpg new file mode 100644 index 0000000..c478a9d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-144.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-145.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-145.jpg new file mode 100644 index 0000000..218426a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-145.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-146.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-146.jpg new file mode 100644 index 0000000..37b0650 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-146.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-147.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-147.jpg new file mode 100644 index 0000000..0b00b64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-147.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-148.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-148.jpg new file mode 100644 index 0000000..0b00b64 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-148.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-149.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-149.jpg new file mode 100644 index 0000000..88347df Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-149.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-150.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-150.jpg new file mode 100644 index 0000000..632b15f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-150.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-151.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-151.jpg new file mode 100644 index 0000000..46119b8 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-151.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-152.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-152.jpg new file mode 100644 index 0000000..97f6354 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-152.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-153.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-153.jpg new file mode 100644 index 0000000..97f6354 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-153.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-154.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-154.jpg new file mode 100644 index 0000000..ed51a36 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-154.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-155.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-155.jpg new file mode 100644 index 0000000..8f92486 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-155.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-156.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-156.jpg new file mode 100644 index 0000000..8e5bb37 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-156.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-157.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-157.jpg new file mode 100644 index 0000000..72a15ef Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-157.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-158.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-158.jpg new file mode 100644 index 0000000..72a15ef Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-158.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-159.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-159.jpg new file mode 100644 index 0000000..2fd18dc Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-159.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-160.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-160.jpg new file mode 100644 index 0000000..4b0471f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-160.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-161.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-161.jpg new file mode 100644 index 0000000..0e25332 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-161.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-162.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-162.jpg new file mode 100644 index 0000000..5e26fc2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-162.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-163.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-163.jpg new file mode 100644 index 0000000..5e26fc2 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-163.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-164.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-164.jpg new file mode 100644 index 0000000..8d69690 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-164.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-165.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-165.jpg new file mode 100644 index 0000000..13574dd Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-165.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-166.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-166.jpg new file mode 100644 index 0000000..a961820 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-166.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-167.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-167.jpg new file mode 100644 index 0000000..58344ad Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-167.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-168.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-168.jpg new file mode 100644 index 0000000..58344ad Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-168.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-169.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-169.jpg new file mode 100644 index 0000000..a64a3a9 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-169.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-170.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-170.jpg new file mode 100644 index 0000000..2e6a520 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-170.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-171.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-171.jpg new file mode 100644 index 0000000..90aed8b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-171.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-172.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-172.jpg new file mode 100644 index 0000000..8563cba Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-172.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-173.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-173.jpg new file mode 100644 index 0000000..8563cba Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-173.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-174.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-174.jpg new file mode 100644 index 0000000..d72651f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-174.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-175.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-175.jpg new file mode 100644 index 0000000..878ae22 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-175.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-176.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-176.jpg new file mode 100644 index 0000000..9b10379 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-176.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-177.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-177.jpg new file mode 100644 index 0000000..fa8dc32 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-177.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-178.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-178.jpg new file mode 100644 index 0000000..fa8dc32 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-178.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-179.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-179.jpg new file mode 100644 index 0000000..fc2d563 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-179.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-180.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-180.jpg new file mode 100644 index 0000000..1c12541 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-180.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-181.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-181.jpg new file mode 100644 index 0000000..4d3cb8b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-181.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-182.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-182.jpg new file mode 100644 index 0000000..ee16f61 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-182.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-183.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-183.jpg new file mode 100644 index 0000000..ee16f61 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-183.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-184.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-184.jpg new file mode 100644 index 0000000..a6d6899 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-184.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-185.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-185.jpg new file mode 100644 index 0000000..f6b984d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-185.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-186.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-186.jpg new file mode 100644 index 0000000..0620171 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-186.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-187.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-187.jpg new file mode 100644 index 0000000..b49343d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-187.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-188.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-188.jpg new file mode 100644 index 0000000..b49343d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-188.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-189.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-189.jpg new file mode 100644 index 0000000..908ce09 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-189.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-190.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-190.jpg new file mode 100644 index 0000000..24d71c6 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-190.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-191.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-191.jpg new file mode 100644 index 0000000..ce5b97f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-191.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-192.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-192.jpg new file mode 100644 index 0000000..2f255be Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-192.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-193.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-193.jpg new file mode 100644 index 0000000..2f255be Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-193.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-194.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-194.jpg new file mode 100644 index 0000000..964a3a3 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-194.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-195.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-195.jpg new file mode 100644 index 0000000..6dd280a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-195.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-196.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-196.jpg new file mode 100644 index 0000000..50c0744 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-196.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-197.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-197.jpg new file mode 100644 index 0000000..a1ab43d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-197.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-198.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-198.jpg new file mode 100644 index 0000000..a1ab43d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-198.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-199.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-199.jpg new file mode 100644 index 0000000..8296e66 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-199.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-200.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-200.jpg new file mode 100644 index 0000000..3b16784 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-200.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-201.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-201.jpg new file mode 100644 index 0000000..f259b43 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-201.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-202.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-202.jpg new file mode 100644 index 0000000..ad17a01 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-202.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-203.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-203.jpg new file mode 100644 index 0000000..ad17a01 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-203.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-204.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-204.jpg new file mode 100644 index 0000000..c452c9a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-204.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-205.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-205.jpg new file mode 100644 index 0000000..a83baf7 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-205.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-206.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-206.jpg new file mode 100644 index 0000000..fe62206 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-206.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-207.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-207.jpg new file mode 100644 index 0000000..be10dd0 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-207.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-208.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-208.jpg new file mode 100644 index 0000000..be10dd0 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-208.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-209.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-209.jpg new file mode 100644 index 0000000..e2b8219 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-209.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-210.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-210.jpg new file mode 100644 index 0000000..c90ef9d Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-210.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-211.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-211.jpg new file mode 100644 index 0000000..b6ddb57 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-211.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-212.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-212.jpg new file mode 100644 index 0000000..30ba939 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-212.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-213.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-213.jpg new file mode 100644 index 0000000..30ba939 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-213.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-214.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-214.jpg new file mode 100644 index 0000000..b7f52f7 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-214.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-215.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-215.jpg new file mode 100644 index 0000000..d0f9974 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-215.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-216.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-216.jpg new file mode 100644 index 0000000..6c4c2dd Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-216.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-217.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-217.jpg new file mode 100644 index 0000000..31e685e Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-217.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-218.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-218.jpg new file mode 100644 index 0000000..31e685e Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-218.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-219.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-219.jpg new file mode 100644 index 0000000..915db56 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-219.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-220.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-220.jpg new file mode 100644 index 0000000..ae3ac04 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-220.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-221.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-221.jpg new file mode 100644 index 0000000..0de0f30 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-221.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-222.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-222.jpg new file mode 100644 index 0000000..59a7641 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-222.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-223.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-223.jpg new file mode 100644 index 0000000..59a7641 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-223.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-224.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-224.jpg new file mode 100644 index 0000000..1c18e80 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-224.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-225.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-225.jpg new file mode 100644 index 0000000..76b1d55 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-225.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-226.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-226.jpg new file mode 100644 index 0000000..e27d565 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-226.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-227.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-227.jpg new file mode 100644 index 0000000..af960f5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-227.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-228.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-228.jpg new file mode 100644 index 0000000..af960f5 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-228.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-229.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-229.jpg new file mode 100644 index 0000000..889537a Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-229.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-230.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-230.jpg new file mode 100644 index 0000000..2f46fd6 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-230.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-231.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-231.jpg new file mode 100644 index 0000000..c9d9026 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-231.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-232.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-232.jpg new file mode 100644 index 0000000..5142693 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-232.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-233.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-233.jpg new file mode 100644 index 0000000..5142693 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-233.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-234.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-234.jpg new file mode 100644 index 0000000..d46b26f Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-234.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-235.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-235.jpg new file mode 100644 index 0000000..55a4a8b Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-235.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-236.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-236.jpg new file mode 100644 index 0000000..050c352 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-236.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-237.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-237.jpg new file mode 100644 index 0000000..92980a1 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-237.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-238.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-238.jpg new file mode 100644 index 0000000..92980a1 Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-238.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-239.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-239.jpg new file mode 100644 index 0000000..1ccc9ba Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-239.jpg differ diff --git a/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-240.jpg b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-240.jpg new file mode 100644 index 0000000..e71e1cb Binary files /dev/null and b/src/frontend/public/assets/images/thinkpad-sequence/ezgif-frame-240.jpg differ diff --git a/src/frontend/public/favicon.ico b/src/frontend/public/favicon.ico new file mode 100644 index 0000000..57614f9 Binary files /dev/null and b/src/frontend/public/favicon.ico differ diff --git a/src/frontend/src/app/app.config.ts b/src/frontend/src/app/app.config.ts new file mode 100644 index 0000000..7cf75c1 --- /dev/null +++ b/src/frontend/src/app/app.config.ts @@ -0,0 +1,15 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter, withComponentInputBinding } from '@angular/router'; +import { provideHttpClient } from '@angular/common/http'; +import { routes } from './app.routes'; +import { RetroScorePort } from './domain/ports/retro-score.port'; +import { LocalStorageRetroScoreAdapter } from './infrastructure/adapters/local-storage-retro-score.adapter'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes, withComponentInputBinding()), + provideHttpClient(), + { provide: RetroScorePort, useClass: LocalStorageRetroScoreAdapter } + ] +}; diff --git a/src/frontend/src/app/app.css b/src/frontend/src/app/app.css new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/app.html b/src/frontend/src/app/app.html new file mode 100644 index 0000000..0680b43 --- /dev/null +++ b/src/frontend/src/app/app.html @@ -0,0 +1 @@ + diff --git a/src/frontend/src/app/app.routes.ts b/src/frontend/src/app/app.routes.ts new file mode 100644 index 0000000..6a2be2e --- /dev/null +++ b/src/frontend/src/app/app.routes.ts @@ -0,0 +1,15 @@ +import { Routes } from '@angular/router'; + +export const routes: Routes = [ + { + path: '', + loadComponent: () => + import('./presentation/features/landing/landing.component').then( + m => m.LandingComponent + ) + }, + { + path: '**', + redirectTo: '' + } +]; diff --git a/src/frontend/src/app/app.spec.ts b/src/frontend/src/app/app.spec.ts new file mode 100644 index 0000000..cc76f56 --- /dev/null +++ b/src/frontend/src/app/app.spec.ts @@ -0,0 +1,23 @@ +import { TestBed } from '@angular/core/testing'; +import { App } from './app'; + +describe('App', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [App], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it('should render router outlet', () => { + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('router-outlet')).toBeTruthy(); + }); +}); diff --git a/src/frontend/src/app/app.ts b/src/frontend/src/app/app.ts new file mode 100644 index 0000000..b298a8b --- /dev/null +++ b/src/frontend/src/app/app.ts @@ -0,0 +1,12 @@ +import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [RouterOutlet], + templateUrl: './app.html', + styleUrl: './app.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class App {} diff --git a/src/frontend/src/app/application/i18n/translations/en.ts b/src/frontend/src/app/application/i18n/translations/en.ts new file mode 100644 index 0000000..eabe73f --- /dev/null +++ b/src/frontend/src/app/application/i18n/translations/en.ts @@ -0,0 +1,232 @@ +import { TranslationDictionary } from '../../../domain/models/i18n.model'; + +export const EN_TRANSLATIONS: TranslationDictionary = { + // Global & Landing + location: 'Quito, EC', + switchLangAria: 'Switch to Spanish', + footerCopy: 'Bryan. Clean Architecture & High-Contrast Design.', + + // Hero 3D + preloading: 'PRELOADING', + heroAriaExperience: 'ThinkPad 3D Scroll Experience', + heroAriaLead: 'Bryan Profile', + heroAriaPhilosophy: 'Technical Philosophy', + heroAriaBackend: 'Backend Architecture', + heroAriaFrontend: 'Reactive Frontend', + heroAriaStandards: 'Technical Standards', + heroAriaInteractive: 'Interactive Sandbox', + p1LeadStatus: '● AVAILABLE // FULLSTACK', + p1LeadTag: '01 // SOFTWARE ENGINEERING', + p1LeadName: 'Bryan Baño', + p1LeadRole: 'Fullstack Software Engineer', + p1LeadDesc: 'Specialist in .NET Clean Architecture and reactive Angular Signals. I engineer high-throughput systems, strict decoupling, and high-performance interactive web experiences.', + p1LeadBadge1: '.NET 9 Clean Arch', + p1LeadBadge2: 'Angular Signals', + p1LeadBadge3: 'MySQL Performance', + p1LeadMetric: 'HEXAGONAL ARCHITECTURE • ZERO BLOAT', + p1RightTag: 'PHILOSOPHY // CORE', + p1RightTitle: 'Simplicity & Performance', + p1RightDesc: 'Ponytail approach (YAGNI / KISS): native platform tools before heavy packages and direct 60 FPS code.', + p1RightBadge: 'Native Web Platform', + p2LeftTag: 'BACKEND CORE', + p2LeftTitle: '.NET 9 & MediatR CQRS', + p2LeftDesc: 'Command Query Responsibility Segregation, FluentValidation rules, and high-throughput transactional queries via EF Core.', + p2LeftBadge1: 'CQRS & MediatR', + p2LeftBadge2: 'Optimized EF Core', + p2RightTag: 'FRONTEND ENGINE', + p2RightTitle: 'Angular 19 & Signals', + p2RightDesc: 'Native reactive state primitives without RxJS overhead, OnPush rendering, and GPU-accelerated 2D Canvas.', + p2RightBadge1: 'Reactive Signals', + p2RightBadge2: 'OnPush 60FPS', + p3LeftTag: '04 // STANDARDS', + p3LeftTitle: 'Patterns & Resilience', + p3LeftDesc: 'Dependency Inversion (DIP), strictly typed DTOs, and uniform ProblemDetails RFC 7807 error handling.', + p3LeftBadge1: 'Dependency Inversion', + p3LeftBadge2: 'RFC 7807', + p3RightTag: '05 // LIVE LAB', + p3RightTitle: 'Interactive Sandbox', + p3RightDesc: 'Embedded interactive Linux terminal with live backend HTTP calls and Retro OS with classic arcade games.', + p3RightBadge1: 'Linux Terminal', + p3RightBadge2: 'Live .NET API', + + // Technical Profile + tpEyebrow: '01 // COMPUTER ENGINEERING & EXPERTISE', + tpTitle: 'Technical Skills & Areas of Interest', + tpLeadStudent: '9th semester Computer Engineering student at', + tpLeadSpecialization: 'Specialized in clean architectures with', + tpLeadStack: '.NET and Angular', + tpLeadEnd: ', network security, and deployments with Docker and AWS.', + tpSkillsSubtitle: 'Technical Skills & Stack', + tpCatBackend: 'Backend:', + tpCatFrontend: 'Frontend:', + tpCatData: 'Data & Cloud:', + tpCatSecurity: 'Security & Networking:', + tpCatSecurityDesc: 'EC-Council (NDE), Cisco Networking Academy (Endpoint Security, Networking).', + tpInterestsSubtitle: 'Interests & Work Environment', + tpInterest1Key: 'Automation & AI:', + tpInterest1Desc: 'Workflow design with multi-agent systems and advanced CLI tools (OpenCode, Claude Code, Antigravity).', + tpInterest2Key: 'Productivity Environments:', + tpInterest2Desc: 'Development on Arch Linux with Fish Shell and technical knowledge management in Obsidian.', + tpInterest3Key: 'Community & Open Source:', + tpInterest3Desc: 'Active participation in tech outreach and community events (FLISOL, Google Developers Group).', + + // Featured Projects + fpHeaderTag: '01 // PROJECTS', + fpAriaLabel: "Bryan Baño's featured projects", + fpPlaceholderImage: 'Image', + fpAltPrefix: 'Screenshot of ', + prj1Subtitle: 'CACES Institutional Accreditation', + prj1Desc: 'Led platform development centralizing evidence upload and validation with advanced security via JWT and SharePoint.', + prj1Impact: 'Automated indicator tracking, eliminating physical paper management with complete traceability for government audits.', + prj2Subtitle: 'Comprehensive Workshop & Work Order Management', + prj2Institution: 'Automotive Sector', + prj2Desc: 'Comprehensive platform for mechanical supply inventory control, vehicle registration, and real-time work order tracking.', + prj2Impact: 'Optimized workshop operational flow, eliminating shortages of critical supplies and improving turnaround times with digital records.', + prj3Subtitle: 'Accelerated Virtual Education', + prj3Institution: 'Academic Platform', + prj3Desc: 'Led academic management platform development based on RESTful API architecture for high availability in remote learning.', + prj3Impact: 'Centralizes academic administration and automates student tracking, eliminating manual enrollment and attendance processes.', + + // Terminal & Workstation + terminalWorkstationTag: '02 // INTERACTIVE WORKSTATION', + terminalHeading: 'Arch Linux Terminal // Fish Shell', + terminalSectionDesc: 'Full-scale interactive console to query technical ecosystem, skills, and live database.', + terminalBanner: ` /\\ bryan@archlinux + / \\ OS: Arch Linux x86_64 + /\\ \\ Host: ThinkPad Z13 Gen 2 + / \\ Kernel: 6.10.7-arch1-1 + / ,, \\ Shell: fish 3.7.0 + / | | -\\ Terminal: zen-alacritty +/_-'' ''-_\\ Type "help" to start.`, + terminalHint: 'Welcome to the interactive Arch Linux / Fish Shell environment.\nType "help" to view available commands.', + terminalHelpContent: `Arch Linux (x86_64) — fish, version 3.7.0 +Available commands for this interactive session: + + help Display this interactive help manual + clear Clear terminal buffer + fetch skills Fetch tech stack in JSON format + cat skills.json View skills and proficiency level + guestbook sign "" Sign interactive guestbook (.NET API) + leaderboard --game snake View ASCII leaderboard (MySQL) + about Engineering philosophy and Bryan's profile + whoami User identity and system permissions + +Fish Shell shortcuts: + [Tab] or [→] Accept dimmed autosuggestion + [↑] / [↓] Navigate command history + Ctrl + L Clear screen`, + terminalAbout: `Bryan Baño — Fullstack Software Engineer +Specialized in .NET Clean Architecture and Angular Signals. +Approach: Zero over-engineering (Ponytail), high throughput, and Zen aesthetic design.`, + terminalGuestbookSignUsage: 'Usage: guestbook sign ""\nExample: guestbook sign "Great architecture!"', + terminalGuestbookGenericUsage: 'Usage: guestbook sign ""\nExample: guestbook sign "Hello from terminal"', + terminalGuestbookSuccess: '✔ Message logged locally: "{msg}"\n[Note: Ready to integrate with POST /api/v1/interactive/guestbook in .NET]', + terminalUnknownCmd: "fish: Unknown command '{cmd}'. Type 'help' to view available commands.", + + // Contact Section + contactEyebrow: 'SOFTWARE ENGINEERING & CLOUD ARCHITECTURE', + contactTitle: 'Custom Software, Clean Architecture & Security by Design', + contactLead: 'Full Stack development (Angular, C# .NET), microservices architectures, AWS cloud optimization, and relational database design. Tell me about your project and I will reply with a technical proposal in under 24 hours.', + contactService1Title: 'Full Stack Development & Clean Architecture', + contactService1Desc: 'Systems built with Angular, Signals, and C# / .NET 8 using Clean Architecture and CQRS to guarantee maintainability and high performance.', + contactService2Title: 'Cloud, Docker & Databases', + contactService2Desc: 'Architectures optimized to run reliably through Docker containers, CI/CD, relational databases (MySQL/PostgreSQL), and AWS services.', + contactFormTitle: 'Request Technical Consultation', + contactFormSubtitle: 'Fill in the form with your company details or project requirements.', + contactSuccessTitle: 'Inquiry Received Successfully!', + contactSuccessDesc: 'Thank you for reaching out. I have registered your requirement and will contact you in under 24 hours with a detailed technical proposal.', + contactSuccessBtn: 'Send another inquiry', + contactLabelName: 'Full Name or Company *', + contactPlaceholderName: 'e.g. Carlos Morales / TechCorp', + contactErrorName: 'Enter your name or company name (min 3 characters).', + contactLabelEmail: 'Email address *', + contactPlaceholderEmail: 'email@example.com', + contactErrorEmail: 'Enter a valid email address.', + contactLabelPhone: 'Phone / WhatsApp (optional)', + contactLabelService: 'What type of service do you need?', + contactServiceSelect: 'Select a service...', + contactServiceBackend: 'Backend Development', + contactServiceFrontend: 'Frontend Development', + contactServiceCloud: 'Cloud Architecture', + contactServiceOther: 'Other', + contactLabelDetails: 'Project details or requirements *', + contactPlaceholderDetails: 'Describe your project objective, timeline, or technical specs...', + contactErrorDetails: 'Describe your requirement (min 10 characters).', + contactFormError: 'Please complete all required fields with a valid format.', + contactBtnSubmitting: 'Processing...', + contactBtnSubmit: 'Send Inquiry', + contactBtnAria: 'Send Technical Consultation', + + // Retro OS + retroStartText: 'start', + retroStartTitle: 'Click here to begin', + retroStartAria: 'Start Button', + retroWmpTitle: 'Windows Media Player', + retroTrayShowHidden: 'Show hidden icons', + retroTraySecurity: 'Security', + retroTrayVolume: 'Volume', + retroTrayNetwork: 'Network', + retroTrayClock: 'Local time', + retroIconGames: 'Games', + retroIconCv: 'Resume', + retroIconScores: 'High Scores', + retroIconSettings: 'Control Panel', + retroWinGames: 'Snake - Windows XP Games', + retroWinCv: 'Curriculum Vitae - Bryan Baño', + retroWinScores: 'Leaderboard - Hall of Fame', + retroWinSettings: 'Control Panel', + retroWinMin: 'Minimize', + retroWinMax: 'Maximize', + retroWinRestore: 'Restore', + retroWinClose: 'Close', + retroMenuInternet: 'Internet', + retroMenuEmail: 'E-mail', + retroMenuGames: 'Games (Snake XP)', + retroMenuWmp: 'Windows Media Player', + retroMenuNotepad: 'Notepad', + retroMenuAllProgs: 'All Programs', + retroMenuDocs: 'My Documents', + retroMenuPics: 'My Pictures', + retroMenuMusic: 'My Music', + retroMenuPc: 'My Computer', + retroMenuSettings: 'Control Panel', + retroMenuScores: 'Arcade Scores', + retroMenuHelp: 'Help and Support', + retroMenuSearch: 'Search', + retroMenuLogoff: 'Log Off', + retroMenuShutdown: 'Turn Off Computer', + retroCvDownload: 'Download CV', + retroCvOpen: 'Open ↗', + retroCvDownloadAria: 'Download Curriculum Vitae in PDF', + retroCvFallback: 'The browser cannot preview the embedded PDF.', + retroSetPalette: 'SYSTEM COLOR PALETTE', + retroSetVisualAudio: 'VISUAL EFFECTS & SOUND', + retroSetScanlines: 'Retro CRT Scanlines', + retroSetScanlinesDesc: 'Analog CRT monitor scanlines', + retroSetSound: 'Web Audio API Sound', + retroSetSoundDesc: 'Synthetic mechanical and arcade effects', + retroScoresHeader: '★ ARCADE LEADERBOARD', + retroScoresTitle: 'ZEN SNAKE // TOP 10', + retroScoresRefresh: 'REFRESH', + retroScoresRank: '#', + retroScoresTag: 'TAG', + retroScoresScore: 'SCORE', + retroScoresDate: 'DATE', + retroScoresEmpty: 'No scores recorded', + retroGameScore: 'SCORE:', + retroGameHigh: 'BEST:', + retroGameStart: '▶ Start', + retroGameRestart: 'Restart', + retroGamePause: 'Pause', + retroGameResume: 'Resume', + retroGameBanner: 'Press any Arrow key or click to start', + retroGameOver: 'GAME OVER!', + retroGameScoreLabel: 'Score:', + retroGameSave: 'Save', + retroGameSaved: '✓ Score saved', + retroGamePlayAgain: '↺ Play Again', + retroGameUp: 'Up', + retroGameLeft: 'Left', + retroGameDown: 'Down', + retroGameRight: 'Right' +}; diff --git a/src/frontend/src/app/application/i18n/translations/es.ts b/src/frontend/src/app/application/i18n/translations/es.ts new file mode 100644 index 0000000..e69df70 --- /dev/null +++ b/src/frontend/src/app/application/i18n/translations/es.ts @@ -0,0 +1,232 @@ +import { TranslationDictionary } from '../../../domain/models/i18n.model'; + +export const ES_TRANSLATIONS: TranslationDictionary = { + // Global & Landing + location: 'Quito, EC', + switchLangAria: 'Cambiar a Inglés', + footerCopy: 'Bryan. Clean Architecture & Diseño de Alto Contraste.', + + // Hero 3D + preloading: 'PRECARGANDO', + heroAriaExperience: 'Experiencia 3D ThinkPad en Scroll', + heroAriaLead: 'Presentación de Bryan', + heroAriaPhilosophy: 'Filosofía Técnica', + heroAriaBackend: 'Arquitectura Backend', + heroAriaFrontend: 'Frontend Reactivo', + heroAriaStandards: 'Estándares Técnicos', + heroAriaInteractive: 'Entorno Interactivo', + p1LeadStatus: '● DISPONIBLE // FULLSTACK', + p1LeadTag: '01 // INGENIERÍA DE SOFTWARE', + p1LeadName: 'Bryan Baño', + p1LeadRole: 'Fullstack Software Engineer', + p1LeadDesc: 'Especialista en .NET Clean Architecture y Angular Signals reactivo. Diseño sistemas empresariales de alta concurrencia, desacoplamiento estricto y experiencias web de máxima fluidez.', + p1LeadBadge1: '.NET 9 Clean Arch', + p1LeadBadge2: 'Angular Signals', + p1LeadBadge3: 'MySQL Performance', + p1LeadMetric: 'ARQUITECTURA HEXAGONAL • CERO SOBREINGENIERÍA', + p1RightTag: 'FILOSOFÍA // CORE', + p1RightTitle: 'Simplicidad & Rendimiento', + p1RightDesc: 'Enfoque Ponytail (YAGNI / KISS): herramientas nativas antes de dependencias pesadas y código directo a 60 FPS.', + p1RightBadge: 'Web Platform Nativa', + p2LeftTag: 'BACKEND CORE', + p2LeftTitle: '.NET 9 & MediatR CQRS', + p2LeftDesc: 'Segregación de comandos y consultas, validación con FluentValidation y mapeo transaccional con EF Core.', + p2LeftBadge1: 'CQRS & MediatR', + p2LeftBadge2: 'EF Core Optimizado', + p2RightTag: 'FRONTEND ENGINE', + p2RightTitle: 'Angular 19 & Signals', + p2RightDesc: 'Gestión de estado nativa con Signals, renderizado OnPush y animaciones por hardware en Canvas 2D.', + p2RightBadge1: 'Signals Reactivos', + p2RightBadge2: 'OnPush 60FPS', + p3LeftTag: 'ESTÁNDARES', + p3LeftTitle: 'Patrones & Resiliencia', + p3LeftDesc: 'Inversión de dependencias (DIP), DTOs estrictamente tipados y manejo uniforme de errores ProblemDetails RFC 7807.', + p3LeftBadge1: 'Inversión Dependencias', + p3LeftBadge2: 'RFC 7807', + p3RightTag: 'LABORATORIO EN VIVO', + p3RightTitle: 'Entorno Interactivo', + p3RightDesc: 'Consola Linux interactiva con llamadas HTTP reales al backend y mini-sistema operativo retro con videojuegos clásicos.', + p3RightBadge1: 'Terminal Linux', + p3RightBadge2: 'Live .NET API', + + // Technical Profile + tpEyebrow: 'INGENIERÍA EN COMPUTACIÓN & EXPERTISE', + tpTitle: 'Habilidades Técnicas & Áreas de Interés', + tpLeadStudent: 'Estudiante de 9no semestre de Ingeniería en Computación en la', + tpLeadSpecialization: 'Especializado en arquitecturas limpias con', + tpLeadStack: '.NET y Angular', + tpLeadEnd: ', seguridad en redes y despliegues con Docker y AWS.', + tpSkillsSubtitle: 'Habilidades Técnicas & Stack', + tpCatBackend: 'Backend:', + tpCatFrontend: 'Frontend:', + tpCatData: 'Datos & Cloud:', + tpCatSecurity: 'Seguridad & Redes:', + tpCatSecurityDesc: 'EC-Council (NDE), Cisco Networking Academy (Endpoint Security, Redes).', + tpInterestsSubtitle: 'Intereses & Entorno de Trabajo', + tpInterest1Key: 'Automatización e IA:', + tpInterest1Desc: 'Diseño de flujos con sistemas multiagente y herramientas CLI avanzadas (OpenCode, Claude Code, Antigravity).', + tpInterest2Key: 'Entornos de Productividad:', + tpInterest2Desc: 'Desarrollo sobre Arch Linux con Fish Shell y gestión del conocimiento técnico en Obsidian.', + tpInterest3Key: 'Comunidad & Open Source:', + tpInterest3Desc: 'Participación activa en eventos de difusión tecnológica (FLISOL, Google Developers Group).', + + // Featured Projects + fpHeaderTag: '01 // PROYECTOS', + fpAriaLabel: 'Proyectos reales de Bryan Baño', + fpPlaceholderImage: 'Imagen', + fpAltPrefix: 'Captura de pantalla de ', + prj1Subtitle: 'Acreditación Institucional CACES', + prj1Desc: 'Lideré el desarrollo de la plataforma centralizando la carga y validación de evidencias con seguridad avanzada mediante JWT y SharePoint.', + prj1Impact: 'Automatizó el seguimiento de indicadores, eliminando la gestión física de documentos con trazabilidad total para auditorías gubernamentales.', + prj2Subtitle: 'Gestión Integral de Talleres & Órdenes', + prj2Institution: 'Sector Automotriz', + prj2Desc: 'Plataforma integral para control de inventarios de insumos mecánicos, registro detallado de vehículos y seguimiento de órdenes de trabajo en tiempo real.', + prj2Impact: 'Optimizó el flujo operativo del taller, eliminando el desabastecimiento de suministros críticos y mejorando los tiempos de entrega con historial digital.', + prj3Subtitle: 'Educación Virtual Acelerada', + prj3Institution: 'Plataforma Académica', + prj3Desc: 'Lideré el desarrollo de la plataforma de gestión académica basada en arquitectura de APIs RESTful para alta disponibilidad en entornos remotos.', + prj3Impact: 'Centraliza la administración académica y automatiza el seguimiento estudiantil, eliminando procesos manuales de inscripción y control de asistencia.', + + // Terminal & Workstation + terminalWorkstationTag: '02 // INTERACTIVE WORKSTATION', + terminalHeading: 'Terminal Arch Linux // Fish Shell', + terminalSectionDesc: 'Consola interactiva a escala completa para consultar el ecosistema técnico, habilidades y base de datos.', + terminalBanner: ` /\\ bryan@archlinux + / \\ OS: Arch Linux x86_64 + /\\ \\ Host: ThinkPad Z13 Gen 2 + / \\ Kernel: 6.10.7-arch1-1 + / ,, \\ Shell: fish 3.7.0 + / | | -\\ Terminal: zen-alacritty +/_-'' ''-_\\ Escribe "help" para comenzar.`, + terminalHint: 'Bienvenido al entorno interactivo Arch Linux / Fish Shell.\nEscribe "help" para ver los comandos disponibles.', + terminalHelpContent: `Arch Linux (x86_64) — fish, version 3.7.0 +Comandos disponibles para la sesión interactiva: + + help Muestra este manual de ayuda interactivo + clear Limpia el buffer de la terminal + fetch skills Obtiene el stack tecnológico en formato JSON + cat skills.json Visualiza las habilidades y nivel de dominio + guestbook sign "" Firma el libro de visitas interactivo (.NET API) + leaderboard --game snake Consulta la tabla ASCII de récords (MySQL) + about Filosofía de ingeniería y perfil de Bryan + whoami Identidad de usuario y permisos del sistema + +Atajos de teclado Fish Shell: + [Tab] o [→] Acepta la autosugerencia en color atenuado + [↑] / [↓] Navega por el historial de comandos + Ctrl + L Limpia la pantalla`, + terminalAbout: `Bryan Baño — Fullstack Software Engineer +Especialista en .NET Clean Architecture y Angular Signals. +Enfoque: Cero sobreingeniería (Ponytail), alta concurrencia y diseño estético Zen.`, + terminalGuestbookSignUsage: 'Uso: guestbook sign ""\nEjemplo: guestbook sign "¡Excelente arquitectura!"', + terminalGuestbookGenericUsage: 'Uso: guestbook sign ""\nEjemplo: guestbook sign "Hola desde la terminal"', + terminalGuestbookSuccess: '✔ Mensaje registrado localmente: "{msg}"\n[Nota: Listo para vincular con POST /api/v1/interactive/guestbook en .NET]', + terminalUnknownCmd: "fish: Comando desconocido '{cmd}'. Escribe 'help' para ver la lista de comandos disponibles.", + + // Contact Section + contactEyebrow: '03 // CONTACTO', + contactTitle: 'Software a Medida, Arquitectura Limpia & Seguridad por Diseño', + contactLead: 'Desarrollo Full Stack (Angular, C# .NET), arquitecturas basadas en microservicios, optimización cloud en AWS y diseño de bases de datos relacionales. Cuéntame tu proyecto y te responderé con una propuesta técnica en menos de 24 horas.', + contactService1Title: 'Desarrollo Full Stack & Arquitectura Limpia', + contactService1Desc: 'Sistemas construidos con Angular, Signals y C# / .NET 8 utilizando Clean Architecture y CQRS para garantizar mantenibilidad y alto rendimiento.', + contactService2Title: 'Cloud, Docker & Bases de Datos', + contactService2Desc: 'Arquitecturas optimizadas para operar de forma estable mediante contenedores Docker, CI/CD, bases de datos relacionales (MySQL/PostgreSQL) y servicios AWS.', + contactFormTitle: 'Solicitar Consulta Técnica', + contactFormSubtitle: 'Rellena el formulario con los detalles de tu empresa o requerimiento.', + contactSuccessTitle: '¡Consulta Recibida con Éxito!', + contactSuccessDesc: 'Gracias por comunicarte. He registrado tu requerimiento y me pondré en contacto contigo en menos de 24 horas con una propuesta técnica detallada.', + contactSuccessBtn: 'Enviar otra consulta', + contactLabelName: 'Nombre completo o Empresa *', + contactPlaceholderName: 'Ej. Carlos Morales / TechCorp', + contactErrorName: 'Ingresa tu nombre o el de tu empresa (mínimo 3 caracteres).', + contactLabelEmail: 'Correo electrónico *', + contactPlaceholderEmail: 'correo@ejemplo.com', + contactErrorEmail: 'Ingresa un correo electrónico válido.', + contactLabelPhone: 'Teléfono / WhatsApp (opcional)', + contactLabelService: '¿Qué tipo de servicio necesitas?', + contactServiceSelect: 'Selecciona un servicio...', + contactServiceBackend: 'Desarrollo Backend', + contactServiceFrontend: 'Desarrollo Frontend', + contactServiceCloud: 'Arquitectura Cloud', + contactServiceOther: 'Otro', + contactLabelDetails: 'Detalles del proyecto o requerimiento *', + contactPlaceholderDetails: 'Describe el objetivo de tu proyecto, plazos o especificaciones técnicas...', + contactErrorDetails: 'Describe tu requerimiento (mínimo 10 caracteres).', + contactFormError: 'Por favor completa todos los campos requeridos con un formato válido.', + contactBtnSubmitting: 'Procesando...', + contactBtnSubmit: 'Enviar Consulta', + contactBtnAria: 'Enviar Consulta Técnica', + + // Retro OS + retroStartText: 'inicio', + retroStartTitle: 'Haga clic aquí para comenzar', + retroStartAria: 'Botón Inicio', + retroWmpTitle: 'Reproductor de Windows Media', + retroTrayShowHidden: 'Mostrar iconos ocultos', + retroTraySecurity: 'Seguridad', + retroTrayVolume: 'Volumen', + retroTrayNetwork: 'Red', + retroTrayClock: 'Hora local', + retroIconGames: 'Juegos', + retroIconCv: 'Curriculum', + retroIconScores: 'Récords', + retroIconSettings: 'Panel de Control', + retroWinGames: 'Snake - Juegos de Windows XP', + retroWinCv: 'Curriculum Vitae - Bryan Baño', + retroWinScores: 'Tabla de Récords - Salón de la Fama', + retroWinSettings: 'Panel de Control', + retroWinMin: 'Minimizar', + retroWinMax: 'Maximizar', + retroWinRestore: 'Restaurar', + retroWinClose: 'Cerrar', + retroMenuInternet: 'Internet', + retroMenuEmail: 'Correo electrónico', + retroMenuGames: 'Juegos (Snake XP)', + retroMenuWmp: 'Reproductor Windows Media', + retroMenuNotepad: 'Bloc de notas', + retroMenuAllProgs: 'Todos los programas', + retroMenuDocs: 'Mis documentos', + retroMenuPics: 'Mis imágenes', + retroMenuMusic: 'Mi música', + retroMenuPc: 'Mi PC', + retroMenuSettings: 'Panel de control', + retroMenuScores: 'Récords Arcade', + retroMenuHelp: 'Ayuda y soporte', + retroMenuSearch: 'Buscar', + retroMenuLogoff: 'Cerrar sesión', + retroMenuShutdown: 'Apagar equipo', + retroCvDownload: 'Descargar CV', + retroCvOpen: 'Abrir ↗', + retroCvDownloadAria: 'Descargar Curriculum Vitae en PDF', + retroCvFallback: 'El navegador no puede previsualizar el PDF embebido.', + retroSetPalette: 'PALETA DE COLOR DEL SISTEMA', + retroSetVisualAudio: 'EFECTOS VISUALES & SONIDO', + retroSetScanlines: 'Scanlines CRT Retro', + retroSetScanlinesDesc: 'Líneas de barrido de monitor CRT analógico', + retroSetSound: 'Sonido Web Audio API', + retroSetSoundDesc: 'Efectos mecánicos y arcade sintéticos', + retroScoresHeader: '★ ARCADE LEADERBOARD', + retroScoresTitle: 'ZEN SNAKE // TOP 10', + retroScoresRefresh: 'ACTUALIZAR', + retroScoresRank: '#', + retroScoresTag: 'TAG', + retroScoresScore: 'PUNTOS', + retroScoresDate: 'FECHA', + retroScoresEmpty: 'No hay puntuaciones registradas', + retroGameScore: 'PUNTOS:', + retroGameHigh: 'RÉCORD:', + retroGameStart: '▶ Iniciar', + retroGameRestart: 'Reiniciar', + retroGamePause: 'Pausa', + retroGameResume: 'Reanudar', + retroGameBanner: 'Pulsa cualquier Flecha o haz clic para empezar', + retroGameOver: '¡FIN DE LA PARTIDA!', + retroGameScoreLabel: 'Puntuación:', + retroGameSave: 'Guardar', + retroGameSaved: '✓ Récord guardado', + retroGamePlayAgain: '↺ Jugar Otra Vez', + retroGameUp: 'Arriba', + retroGameLeft: 'Izquierda', + retroGameDown: 'Abajo', + retroGameRight: 'Derecha' +}; diff --git a/src/frontend/src/app/application/services/.gitkeep b/src/frontend/src/app/application/services/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/application/services/i18n.service.ts b/src/frontend/src/app/application/services/i18n.service.ts new file mode 100644 index 0000000..5a0021d --- /dev/null +++ b/src/frontend/src/app/application/services/i18n.service.ts @@ -0,0 +1,48 @@ +import { Injectable, signal, computed } from '@angular/core'; +import { Language, TranslationDictionary } from '../../domain/models/i18n.model'; +import { ES_TRANSLATIONS } from '../i18n/translations/es'; +import { EN_TRANSLATIONS } from '../i18n/translations/en'; + +export type { Language, TranslationDictionary }; + +const TRANSLATIONS: Record = { + es: ES_TRANSLATIONS, + en: EN_TRANSLATIONS +}; + +@Injectable({ providedIn: 'root' }) +export class I18nService { + readonly currentLang = signal(this.getSavedLanguage()); + readonly t = computed(() => TRANSLATIONS[this.currentLang()]); + + toggleLanguage(): void { + this.currentLang.update(lang => { + const next: Language = lang === 'es' ? 'en' : 'es'; + try { + localStorage.setItem('portfolio_lang', next); + } catch { + // Fallback si localStorage no está disponible + } + return next; + }); + } + + setLanguage(lang: Language): void { + this.currentLang.set(lang); + try { + localStorage.setItem('portfolio_lang', lang); + } catch { + // Fallback + } + } + + private getSavedLanguage(): Language { + try { + const saved = localStorage.getItem('portfolio_lang'); + if (saved === 'es' || saved === 'en') return saved; + } catch { + // Fallback + } + return 'es'; + } +} diff --git a/src/frontend/src/app/application/services/retro-audio.service.ts b/src/frontend/src/app/application/services/retro-audio.service.ts new file mode 100644 index 0000000..d9428fd --- /dev/null +++ b/src/frontend/src/app/application/services/retro-audio.service.ts @@ -0,0 +1,104 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ providedIn: 'root' }) +export class RetroAudioService { + private ctx: AudioContext | null = null; + private isMuted = false; + + setMuted(muted: boolean): void { + this.isMuted = muted; + } + + private getContext(): AudioContext | null { + if (this.isMuted || typeof window === 'undefined') return null; + if (!this.ctx) { + const AudioContextClass = window.AudioContext || (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext; + if (AudioContextClass) { + this.ctx = new AudioContextClass(); + } + } + if (this.ctx && this.ctx.state === 'suspended') { + void this.ctx.resume(); + } + return this.ctx; + } + + playClick(): void { + const ctx = this.getContext(); + if (!ctx) return; + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + + osc.type = 'triangle'; + osc.frequency.setValueAtTime(140, ctx.currentTime); + osc.frequency.exponentialRampToValueAtTime(80, ctx.currentTime + 0.02); + + gain.gain.setValueAtTime(0.04, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.02); + + osc.connect(gain); + gain.connect(ctx.destination); + osc.start(); + osc.stop(ctx.currentTime + 0.02); + } + + playSnakeEat(): void { + const ctx = this.getContext(); + if (!ctx) return; + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + + osc.type = 'sine'; + osc.frequency.setValueAtTime(440, ctx.currentTime); + osc.frequency.exponentialRampToValueAtTime(880, ctx.currentTime + 0.08); + + gain.gain.setValueAtTime(0.06, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.08); + + osc.connect(gain); + gain.connect(ctx.destination); + osc.start(); + osc.stop(ctx.currentTime + 0.08); + } + + playGameOver(): void { + const ctx = this.getContext(); + if (!ctx) return; + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + + osc.type = 'sawtooth'; + osc.frequency.setValueAtTime(320, ctx.currentTime); + osc.frequency.exponentialRampToValueAtTime(90, ctx.currentTime + 0.25); + + gain.gain.setValueAtTime(0.06, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.25); + + osc.connect(gain); + gain.connect(ctx.destination); + osc.start(); + osc.stop(ctx.currentTime + 0.25); + } + + playHighScore(): void { + const ctx = this.getContext(); + if (!ctx) return; + const notes = [523.25, 659.25, 783.99]; // Do, Mi, Sol + notes.forEach((freq, index) => { + const startTime = ctx.currentTime + index * 0.07; + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + + osc.type = 'square'; + osc.frequency.setValueAtTime(freq, startTime); + + gain.gain.setValueAtTime(0.03, startTime); + gain.gain.exponentialRampToValueAtTime(0.001, startTime + 0.06); + + osc.connect(gain); + gain.connect(ctx.destination); + osc.start(startTime); + osc.stop(startTime + 0.06); + }); + } +} diff --git a/src/frontend/src/app/application/services/terminal-executor.service.ts b/src/frontend/src/app/application/services/terminal-executor.service.ts new file mode 100644 index 0000000..1a78864 --- /dev/null +++ b/src/frontend/src/app/application/services/terminal-executor.service.ts @@ -0,0 +1,153 @@ +import { Injectable, inject } from '@angular/core'; +import { I18nService } from './i18n.service'; + +export type CommandExecutionResult = + | { readonly type: 'clear'; readonly content?: string } + | { readonly type: 'output' | 'error' | 'success' | 'ascii'; readonly content: string }; + +@Injectable({ providedIn: 'root' }) +export class TerminalExecutorService { + private readonly i18n = inject(I18nService); + + private readonly availableCommands = [ + 'help', + 'clear', + 'fetch skills', + 'cat skills.json', + 'guestbook sign "', + 'leaderboard --game snake', + 'about', + 'whoami' + ]; + + getSuggestions(partial: string): string { + const trimmed = partial.trimStart(); + if (!trimmed) return ''; + + const match = this.availableCommands.find(cmd => + cmd.toLowerCase().startsWith(trimmed.toLowerCase()) + ); + + if (match && match.length > trimmed.length) { + return match.slice(trimmed.length); + } + + return ''; + } + + execute(rawInput: string): CommandExecutionResult { + const trimmed = rawInput.trim(); + if (!trimmed) { + return { type: 'output', content: '' }; + } + + const [cmd, ...args] = trimmed.split(/\s+/); + const lowerCmd = cmd.toLowerCase(); + const fullCommand = trimmed.toLowerCase(); + const t = this.i18n.t(); + + // 1. Comando CLEAR + if (lowerCmd === 'clear') { + return { type: 'clear', content: '' }; + } + + // 2. Comando HELP + if (lowerCmd === 'help') { + return { + type: 'output', + content: t.terminalHelpContent + }; + } + + // 3. Comandos de Skills (cat skills.json / fetch skills) + if (fullCommand === 'cat skills.json' || fullCommand === 'fetch skills') { + return { + type: 'output', + content: `{ + "developer": "Bryan Baño", + "backend": { + "framework": ".NET 8 / .NET 9", + "patterns": ["Clean Architecture", "CQRS", "MediatR", "Domain-Driven Design"], + "data": ["Entity Framework Core", "MySQL", "PostgreSQL", "Redis"], + "quality": ["FluentValidation", "TDD", "xUnit", "ProblemDetails RFC 7807"] + }, + "frontend": { + "framework": "Angular 19/20", + "reactivity": ["Signals", "computed", "effect", "OnPush 60FPS"], + "architecture": ["Ports & Adapters (Hexagonal)", "Feature Slicing"], + "multimedia": ["HTML5 Canvas 2D", "Web Audio API", "Three.js / WebGL"] + }, + "devops_cloud": ["Docker", "Linux / Arch", "Nginx", "GitHub Actions CI/CD"] +}` + }; + } + + // 4. Comando Guestbook + if (lowerCmd === 'guestbook') { + const subAction = args[0]?.toLowerCase(); + if (subAction === 'sign') { + const messageMatch = trimmed.match(/sign\s+["']?([^"']+)["']?/i); + const message = messageMatch ? messageMatch[1] : args.slice(1).join(' '); + if (!message) { + return { + type: 'error', + content: t.terminalGuestbookSignUsage + }; + } + return { + type: 'success', + content: t.terminalGuestbookSuccess.replace('{msg}', message) + }; + } + return { + type: 'error', + content: t.terminalGuestbookGenericUsage + }; + } + + // 5. Comando Leaderboard (Tabla ASCII de récords) + if (lowerCmd === 'leaderboard') { + const isEs = this.i18n.currentLang() === 'es'; + const playerCol = isEs ? 'JUGADOR ' : 'PLAYER '; + const gameCol = isEs ? 'JUEGO ' : 'GAME '; + const note = isEs + ? '(4 filas consultadas - Listo para enlazar con GET /api/v1/scores)' + : '(4 rows retrieved - Ready to bind with GET /api/v1/scores)'; + + return { + type: 'output', + content: `+------+---------------------+-------+---------------------+ +| POS | ${playerCol}| PTS | ${gameCol}| ++------+---------------------+-------+---------------------+ +| 1 | Bryan Baño | 9,840 | Zen Snake CRT | +| 2 | Neo_Arch | 8,420 | Zen Snake CRT | +| 3 | KintsugiDev | 6,150 | Zen Snake CRT | +| 4 | Guest_404 | 3,800 | Zen Snake CRT | ++------+---------------------+-------+---------------------+ +${note}` + }; + } + + // 6. Comando About + if (lowerCmd === 'about') { + return { + type: 'output', + content: t.terminalAbout + }; + } + + // 7. Comando Whoami + if (lowerCmd === 'whoami') { + return { + type: 'output', + content: 'bryan@archlinux (terminal-guest, uid=1000, groups=wheel,developers)' + }; + } + + // Comando no reconocido + return { + type: 'error', + content: t.terminalUnknownCmd.replace('{cmd}', cmd) + }; + } +} diff --git a/src/frontend/src/app/application/stores/.gitkeep b/src/frontend/src/app/application/stores/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/application/stores/retro-os.store.ts b/src/frontend/src/app/application/stores/retro-os.store.ts new file mode 100644 index 0000000..cb2f95d --- /dev/null +++ b/src/frontend/src/app/application/stores/retro-os.store.ts @@ -0,0 +1,249 @@ +import { Injectable, signal, computed, inject, effect } from '@angular/core'; +import { WindowId, RetroWindow, DesktopIcon, ZenOsTheme, ZenOsConfig, WindowPosition, WindowSize } from '../../domain/models/retro-os.model'; +import { RetroAudioService } from '../services/retro-audio.service'; +import { I18nService } from '../services/i18n.service'; + +const INITIAL_WINDOWS: Record = { + games: { + id: 'games', + title: 'Snake - Juegos de Windows XP', + icon: 'gamepad', + isOpen: false, + isMinimized: false, + position: { x: 70, y: 20 }, + size: { width: 360, height: 490 }, + zIndex: 10, + minSize: { width: 320, height: 380 } + }, + cv: { + id: 'cv', + title: 'Curriculum Vitae - Bryan Baño', + icon: 'document', + isOpen: false, + isMinimized: false, + isMaximized: true, + position: { x: 25, y: 15 }, + size: { width: 450, height: 420 }, + zIndex: 11, + minSize: { width: 320, height: 300 } + }, + scores: { + id: 'scores', + title: 'Tabla de Récords - Salón de la Fama', + icon: 'trophy', + isOpen: false, + isMinimized: false, + position: { x: 120, y: 50 }, + size: { width: 340, height: 380 }, + zIndex: 12, + minSize: { width: 280, height: 300 } + }, + settings: { + id: 'settings', + title: 'Panel de Control', + icon: 'gear', + isOpen: false, + isMinimized: false, + position: { x: 140, y: 65 }, + size: { width: 320, height: 320 }, + zIndex: 13, + minSize: { width: 280, height: 260 } + } +}; + +@Injectable({ providedIn: 'root' }) +export class RetroOsStore { + private readonly audio = inject(RetroAudioService); + private readonly i18n = inject(I18nService); + + private readonly _windows = signal>(INITIAL_WINDOWS); + private readonly _activeWindowId = signal(null); + private readonly _topZIndex = signal(20); + + private readonly _theme = signal(this.getSavedTheme()); + private readonly _scanlines = signal(this.getSavedBoolean('zenos_scanlines', true)); + private readonly _soundEnabled = signal(this.getSavedBoolean('zenos_sound', true)); + + readonly desktopIcons = computed(() => { + const t = this.i18n.t(); + return [ + { id: 'icon-games', label: t.retroIconGames, windowId: 'games', iconType: 'gamepad' }, + { id: 'icon-cv', label: t.retroIconCv, windowId: 'cv', iconType: 'document' }, + { id: 'icon-scores', label: t.retroIconScores, windowId: 'scores', iconType: 'trophy' }, + { id: 'icon-settings', label: t.retroIconSettings, windowId: 'settings', iconType: 'gear' } + ]; + }); + readonly windows = this._windows.asReadonly(); + readonly activeWindowId = this._activeWindowId.asReadonly(); + readonly theme = this._theme.asReadonly(); + readonly scanlines = this._scanlines.asReadonly(); + readonly soundEnabled = this._soundEnabled.asReadonly(); + + readonly openWindows = computed(() => + Object.values(this._windows()).filter(w => w.isOpen) + ); + + readonly activeWindow = computed(() => { + const id = this._activeWindowId(); + return id ? this._windows()[id] : null; + }); + + constructor() { + this.applyThemeToDocument(this._theme()); + this.audio.setMuted(!this._soundEnabled()); + + effect(() => { + const t = this.i18n.t(); + this._windows.update(prev => ({ + ...prev, + games: { ...prev.games, title: t.retroWinGames }, + cv: { ...prev.cv, title: t.retroWinCv }, + scores: { ...prev.scores, title: t.retroWinScores }, + settings: { ...prev.settings, title: t.retroWinSettings } + })); + }); + } + + openWindow(id: WindowId): void { + this.audio.playClick(); + const nextZ = this._topZIndex() + 1; + this._topZIndex.set(nextZ); + this._activeWindowId.set(id); + + this._windows.update(prev => ({ + ...prev, + [id]: { + ...prev[id], + isOpen: true, + isMinimized: false, + isMaximized: id === 'cv' ? true : prev[id].isMaximized, + zIndex: nextZ + } + })); + } + + toggleMaximize(id: WindowId): void { + this.audio.playClick(); + this._windows.update(prev => ({ + ...prev, + [id]: { + ...prev[id], + isMaximized: !prev[id].isMaximized + } + })); + } + + closeWindow(id: WindowId): void { + this.audio.playClick(); + this._windows.update(prev => ({ + ...prev, + [id]: { ...prev[id], isOpen: false } + })); + + if (this._activeWindowId() === id) { + const remaining = this.openWindows().filter(w => w.id !== id && !w.isMinimized); + const nextActive = remaining.sort((a, b) => b.zIndex - a.zIndex)[0]; + this._activeWindowId.set(nextActive ? nextActive.id : null); + } + } + + minimizeWindow(id: WindowId): void { + this.audio.playClick(); + this._windows.update(prev => ({ + ...prev, + [id]: { ...prev[id], isMinimized: true } + })); + + if (this._activeWindowId() === id) { + const remaining = this.openWindows().filter(w => w.id !== id && !w.isMinimized); + const nextActive = remaining.sort((a, b) => b.zIndex - a.zIndex)[0]; + this._activeWindowId.set(nextActive ? nextActive.id : null); + } + } + + focusWindow(id: WindowId): void { + if (this._activeWindowId() === id) return; + this.audio.playClick(); + const nextZ = this._topZIndex() + 1; + this._topZIndex.set(nextZ); + this._activeWindowId.set(id); + + this._windows.update(prev => ({ + ...prev, + [id]: { ...prev[id], isMinimized: false, zIndex: nextZ } + })); + } + + moveWindow(id: WindowId, position: WindowPosition): void { + this._windows.update(prev => ({ + ...prev, + [id]: { ...prev[id], position } + })); + } + + setTheme(theme: ZenOsTheme): void { + this.audio.playClick(); + this._theme.set(theme); + this.applyThemeToDocument(theme); + try { + localStorage.setItem('zenos_theme', theme); + } catch { + // Ignorar fallo de almacenamiento + } + } + + toggleScanlines(): void { + this.audio.playClick(); + this._scanlines.update(v => { + const next = !v; + try { + localStorage.setItem('zenos_scanlines', String(next)); + } catch { + // Ignorar + } + return next; + }); + } + + toggleSound(): void { + this._soundEnabled.update(v => { + const next = !v; + this.audio.setMuted(!next); + if (next) this.audio.playClick(); + try { + localStorage.setItem('zenos_sound', String(next)); + } catch { + // Ignorar + } + return next; + }); + } + + private applyThemeToDocument(theme: ZenOsTheme): void { + if (typeof document !== 'undefined' && document.documentElement) { + document.documentElement.setAttribute('data-theme', theme); + } + } + + private getSavedTheme(): ZenOsTheme { + try { + const saved = localStorage.getItem('zenos_theme'); + if (saved === 'zen-dark' || saved === 'retro-amber' || saved === 'matrix-green') { + return saved; + } + } catch { + // Fallback + } + return 'zen-dark'; + } + + private getSavedBoolean(key: string, fallback: boolean): boolean { + try { + const saved = localStorage.getItem(key); + if (saved !== null) return saved === 'true'; + } catch { + // Fallback + } + return fallback; + } +} diff --git a/src/frontend/src/app/core/guards/.gitkeep b/src/frontend/src/app/core/guards/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/core/interceptors/.gitkeep b/src/frontend/src/app/core/interceptors/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/core/tokens/.gitkeep b/src/frontend/src/app/core/tokens/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/domain/models/.gitkeep b/src/frontend/src/app/domain/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/domain/models/i18n.model.ts b/src/frontend/src/app/domain/models/i18n.model.ts new file mode 100644 index 0000000..f9cdedf --- /dev/null +++ b/src/frontend/src/app/domain/models/i18n.model.ts @@ -0,0 +1,209 @@ +export type Language = 'es' | 'en'; + +export interface TranslationDictionary { + // Global & Landing + readonly location: string; + readonly switchLangAria: string; + readonly footerCopy: string; + + // Hero 3D + readonly preloading: string; + readonly heroAriaExperience: string; + readonly heroAriaLead: string; + readonly heroAriaPhilosophy: string; + readonly heroAriaBackend: string; + readonly heroAriaFrontend: string; + readonly heroAriaStandards: string; + readonly heroAriaInteractive: string; + readonly p1LeadStatus: string; + readonly p1LeadTag: string; + readonly p1LeadName: string; + readonly p1LeadRole: string; + readonly p1LeadDesc: string; + readonly p1LeadBadge1: string; + readonly p1LeadBadge2: string; + readonly p1LeadBadge3: string; + readonly p1LeadMetric: string; + readonly p1RightTag: string; + readonly p1RightTitle: string; + readonly p1RightDesc: string; + readonly p1RightBadge: string; + readonly p2LeftTag: string; + readonly p2LeftTitle: string; + readonly p2LeftDesc: string; + readonly p2LeftBadge1: string; + readonly p2LeftBadge2: string; + readonly p2RightTag: string; + readonly p2RightTitle: string; + readonly p2RightDesc: string; + readonly p2RightBadge1: string; + readonly p2RightBadge2: string; + readonly p3LeftTag: string; + readonly p3LeftTitle: string; + readonly p3LeftDesc: string; + readonly p3LeftBadge1: string; + readonly p3LeftBadge2: string; + readonly p3RightTag: string; + readonly p3RightTitle: string; + readonly p3RightDesc: string; + readonly p3RightBadge1: string; + readonly p3RightBadge2: string; + + // Technical Profile + readonly tpEyebrow: string; + readonly tpTitle: string; + readonly tpLeadStudent: string; + readonly tpLeadSpecialization: string; + readonly tpLeadStack: string; + readonly tpLeadEnd: string; + readonly tpSkillsSubtitle: string; + readonly tpCatBackend: string; + readonly tpCatFrontend: string; + readonly tpCatData: string; + readonly tpCatSecurity: string; + readonly tpCatSecurityDesc: string; + readonly tpInterestsSubtitle: string; + readonly tpInterest1Key: string; + readonly tpInterest1Desc: string; + readonly tpInterest2Key: string; + readonly tpInterest2Desc: string; + readonly tpInterest3Key: string; + readonly tpInterest3Desc: string; + + // Featured Projects + readonly fpHeaderTag: string; + readonly fpAriaLabel: string; + readonly fpPlaceholderImage: string; + readonly fpAltPrefix: string; + readonly prj1Subtitle: string; + readonly prj1Desc: string; + readonly prj1Impact: string; + readonly prj2Subtitle: string; + readonly prj2Institution: string; + readonly prj2Desc: string; + readonly prj2Impact: string; + readonly prj3Subtitle: string; + readonly prj3Institution: string; + readonly prj3Desc: string; + readonly prj3Impact: string; + + // Terminal & Workstation + readonly terminalWorkstationTag: string; + readonly terminalHeading: string; + readonly terminalSectionDesc: string; + readonly terminalBanner: string; + readonly terminalHint: string; + readonly terminalHelpContent: string; + readonly terminalAbout: string; + readonly terminalGuestbookSignUsage: string; + readonly terminalGuestbookGenericUsage: string; + readonly terminalGuestbookSuccess: string; + readonly terminalUnknownCmd: string; + + // Contact Section + readonly contactEyebrow: string; + readonly contactTitle: string; + readonly contactLead: string; + readonly contactService1Title: string; + readonly contactService1Desc: string; + readonly contactService2Title: string; + readonly contactService2Desc: string; + readonly contactFormTitle: string; + readonly contactFormSubtitle: string; + readonly contactSuccessTitle: string; + readonly contactSuccessDesc: string; + readonly contactSuccessBtn: string; + readonly contactLabelName: string; + readonly contactPlaceholderName: string; + readonly contactErrorName: string; + readonly contactLabelEmail: string; + readonly contactPlaceholderEmail: string; + readonly contactErrorEmail: string; + readonly contactLabelPhone: string; + readonly contactLabelService: string; + readonly contactServiceSelect: string; + readonly contactServiceBackend: string; + readonly contactServiceFrontend: string; + readonly contactServiceCloud: string; + readonly contactServiceOther: string; + readonly contactLabelDetails: string; + readonly contactPlaceholderDetails: string; + readonly contactErrorDetails: string; + readonly contactFormError: string; + readonly contactBtnSubmitting: string; + readonly contactBtnSubmit: string; + readonly contactBtnAria: string; + + // Retro OS + readonly retroStartText: string; + readonly retroStartTitle: string; + readonly retroStartAria: string; + readonly retroWmpTitle: string; + readonly retroTrayShowHidden: string; + readonly retroTraySecurity: string; + readonly retroTrayVolume: string; + readonly retroTrayNetwork: string; + readonly retroTrayClock: string; + readonly retroIconGames: string; + readonly retroIconCv: string; + readonly retroIconScores: string; + readonly retroIconSettings: string; + readonly retroWinGames: string; + readonly retroWinCv: string; + readonly retroWinScores: string; + readonly retroWinSettings: string; + readonly retroWinMin: string; + readonly retroWinMax: string; + readonly retroWinRestore: string; + readonly retroWinClose: string; + readonly retroMenuInternet: string; + readonly retroMenuEmail: string; + readonly retroMenuGames: string; + readonly retroMenuWmp: string; + readonly retroMenuNotepad: string; + readonly retroMenuAllProgs: string; + readonly retroMenuDocs: string; + readonly retroMenuPics: string; + readonly retroMenuMusic: string; + readonly retroMenuPc: string; + readonly retroMenuSettings: string; + readonly retroMenuScores: string; + readonly retroMenuHelp: string; + readonly retroMenuSearch: string; + readonly retroMenuLogoff: string; + readonly retroMenuShutdown: string; + readonly retroCvDownload: string; + readonly retroCvOpen: string; + readonly retroCvDownloadAria: string; + readonly retroCvFallback: string; + readonly retroSetPalette: string; + readonly retroSetVisualAudio: string; + readonly retroSetScanlines: string; + readonly retroSetScanlinesDesc: string; + readonly retroSetSound: string; + readonly retroSetSoundDesc: string; + readonly retroScoresHeader: string; + readonly retroScoresTitle: string; + readonly retroScoresRefresh: string; + readonly retroScoresRank: string; + readonly retroScoresTag: string; + readonly retroScoresScore: string; + readonly retroScoresDate: string; + readonly retroScoresEmpty: string; + readonly retroGameScore: string; + readonly retroGameHigh: string; + readonly retroGameStart: string; + readonly retroGameRestart: string; + readonly retroGamePause: string; + readonly retroGameResume: string; + readonly retroGameBanner: string; + readonly retroGameOver: string; + readonly retroGameScoreLabel: string; + readonly retroGameSave: string; + readonly retroGameSaved: string; + readonly retroGamePlayAgain: string; + readonly retroGameUp: string; + readonly retroGameLeft: string; + readonly retroGameDown: string; + readonly retroGameRight: string; +} diff --git a/src/frontend/src/app/domain/models/retro-game.model.ts b/src/frontend/src/app/domain/models/retro-game.model.ts new file mode 100644 index 0000000..db3b59d --- /dev/null +++ b/src/frontend/src/app/domain/models/retro-game.model.ts @@ -0,0 +1,31 @@ +export interface Point { + readonly x: number; + readonly y: number; +} + +export type Direction = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'; + +export type GameDifficulty = 'ZEN' | 'STANDARD' | 'TURBO'; + +export interface FoodItem { + readonly position: Point; + readonly kanji: string; + readonly points: number; +} + +export interface RetroScore { + readonly id: string; + readonly playerTag: string; + readonly gameCode: string; + readonly score: number; + readonly createdAtUtc: string; +} + +export interface GameState { + readonly score: number; + readonly highScore: number; + readonly isGameOver: boolean; + readonly isPaused: boolean; + readonly isRunning: boolean; + readonly difficulty: GameDifficulty; +} diff --git a/src/frontend/src/app/domain/models/retro-os.model.ts b/src/frontend/src/app/domain/models/retro-os.model.ts new file mode 100644 index 0000000..bd95154 --- /dev/null +++ b/src/frontend/src/app/domain/models/retro-os.model.ts @@ -0,0 +1,39 @@ +export type WindowId = 'games' | 'cv' | 'settings' | 'scores'; + +export type ZenOsTheme = 'zen-dark' | 'retro-amber' | 'matrix-green'; + +export interface WindowPosition { + readonly x: number; + readonly y: number; +} + +export interface WindowSize { + readonly width: number; + readonly height: number; +} + +export interface RetroWindow { + readonly id: WindowId; + readonly title: string; + readonly icon: string; + readonly isOpen: boolean; + readonly isMinimized: boolean; + readonly isMaximized?: boolean; + readonly position: WindowPosition; + readonly size: WindowSize; + readonly zIndex: number; + readonly minSize?: WindowSize; +} + +export interface DesktopIcon { + readonly id: string; + readonly label: string; + readonly windowId: WindowId; + readonly iconType: 'gamepad' | 'document' | 'terminal' | 'gear' | 'trophy'; +} + +export interface ZenOsConfig { + readonly theme: ZenOsTheme; + readonly crtScanlinesEnabled: boolean; + readonly soundMuted: boolean; +} diff --git a/src/frontend/src/app/domain/ports/.gitkeep b/src/frontend/src/app/domain/ports/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/domain/ports/retro-score.port.ts b/src/frontend/src/app/domain/ports/retro-score.port.ts new file mode 100644 index 0000000..215741b --- /dev/null +++ b/src/frontend/src/app/domain/ports/retro-score.port.ts @@ -0,0 +1,7 @@ +import { Observable } from 'rxjs'; +import { RetroScore } from '../models/retro-game.model'; + +export abstract class RetroScorePort { + abstract getTopScores(gameCode: string, limit?: number): Observable; + abstract saveScore(score: Omit): Observable; +} diff --git a/src/frontend/src/app/infrastructure/adapters/.gitkeep b/src/frontend/src/app/infrastructure/adapters/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/infrastructure/adapters/local-storage-retro-score.adapter.ts b/src/frontend/src/app/infrastructure/adapters/local-storage-retro-score.adapter.ts new file mode 100644 index 0000000..8d49719 --- /dev/null +++ b/src/frontend/src/app/infrastructure/adapters/local-storage-retro-score.adapter.ts @@ -0,0 +1,75 @@ +import { Injectable } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { RetroScorePort } from '../../domain/ports/retro-score.port'; +import { RetroScore } from '../../domain/models/retro-game.model'; + +const STORAGE_KEY = 'zenos_retro_scores'; + +const INITIAL_SEEDS: RetroScore[] = [ + { + id: 'seed-1', + playerTag: 'ZEN', + gameCode: 'ZEN_SNAKE', + score: 1450, + createdAtUtc: '2026-08-28T00:00:00.000Z' + }, + { + id: 'seed-2', + playerTag: 'NEO', + gameCode: 'ZEN_SNAKE', + score: 1120, + createdAtUtc: '2026-08-28T00:00:00.000Z' + }, + { + id: 'seed-3', + playerTag: 'BRY', + gameCode: 'ZEN_SNAKE', + score: 980, + createdAtUtc: '2026-08-28T00:00:00.000Z' + } +]; + +@Injectable({ providedIn: 'root' }) +export class LocalStorageRetroScoreAdapter implements RetroScorePort { + private readScores(): RetroScore[] { + try { + const raw = localStorage.getItem(STORAGE_KEY); + if (!raw) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(INITIAL_SEEDS)); + return INITIAL_SEEDS; + } + return JSON.parse(raw) as RetroScore[]; + } catch { + return INITIAL_SEEDS; + } + } + + private writeScores(scores: RetroScore[]): void { + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(scores)); + } catch { + // Ignorar fallo de cuota en entornos restringidos + } + } + + getTopScores(gameCode: string, limit = 10): Observable { + const scores = this.readScores(); + const filtered = scores + .filter(s => s.gameCode === gameCode) + .sort((a, b) => b.score - a.score || a.createdAtUtc.localeCompare(b.createdAtUtc)) + .slice(0, limit); + return of(filtered); + } + + saveScore(data: Omit): Observable { + const scores = this.readScores(); + const newEntry: RetroScore = { + ...data, + id: typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : `score-${Date.now()}`, + createdAtUtc: new Date().toISOString() + }; + scores.push(newEntry); + this.writeScores(scores); + return of(newEntry); + } +} diff --git a/src/frontend/src/app/infrastructure/dtos/.gitkeep b/src/frontend/src/app/infrastructure/dtos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/infrastructure/mappers/.gitkeep b/src/frontend/src/app/infrastructure/mappers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/presentation/features/contact-section/contact-section.component.css b/src/frontend/src/app/presentation/features/contact-section/contact-section.component.css new file mode 100644 index 0000000..1b34c8e --- /dev/null +++ b/src/frontend/src/app/presentation/features/contact-section/contact-section.component.css @@ -0,0 +1,344 @@ +:host { + display: block; + width: 100%; +} + +/* 1. CONTENEDOR PRINCIPAL NORMALIZADO (Fondo #121517 & Padding Amplio) */ +.contact-section { + width: 100%; + background-color: #121517; + padding: clamp(6rem, 10vw, 8rem) 0; /* py-24 sm:py-32 */ + border-top: 1px solid rgba(255, 255, 255, 0.04); +} + +.contact-container { + max-width: 80rem; /* max-w-7xl */ + margin: 0 auto; + padding: 0 1.5rem; /* px-6 */ +} + +@media (min-width: 1024px) { + .contact-container { + padding: 0 2rem; /* lg:px-8 */ + } +} + +.contact-grid { + display: grid; + grid-template-columns: 1fr; + gap: 4rem; /* gap-16 */ + align-items: center; +} + +@media (min-width: 1024px) { + .contact-grid { + grid-template-columns: 1fr 1fr; + gap: 6rem; /* lg:gap-24 */ + } +} + +/* 2. COLUMNA IZQUIERDA (Textos en Paleta Zen Dark) */ +.contact-info-col { + display: flex; + flex-direction: column; +} + +.contact-eyebrow { + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0.14em; /* tracking-widest */ + color: var(--zen-accent-kintsugi); /* Dorado Kintsugi Normalizado */ + text-transform: uppercase; + margin-bottom: 1.25rem; + display: inline-block; +} + +.contact-title { + font-family: var(--font-sans); + font-size: clamp(2.25rem, 4.5vw, 3.5rem); /* text-5xl o text-6xl */ + font-weight: 800; /* font-bold */ + color: var(--zen-text-primary); + line-height: 1.15; /* leading-tight */ + letter-spacing: -0.02em; + margin: 0 0 1.5rem 0; +} + +.contact-lead { + font-size: 1.0625rem; /* text-lg */ + color: var(--zen-text-secondary); + line-height: 1.7; + margin: 0 0 2rem 0; + max-width: 580px; +} + +.service-block { + margin-top: 1.5rem; +} + +.service-block:first-of-type { + margin-top: 2rem; /* mt-10 */ +} + +.service-title { + font-family: var(--font-sans); + font-size: 1.2rem; /* text-xl */ + font-weight: 700; /* font-bold */ + color: var(--zen-text-primary); + margin: 0 0 0.5rem 0; + display: flex; + align-items: baseline; + gap: 8px; +} + +.service-title::before { + content: '▹'; + color: var(--zen-accent-kintsugi); + font-size: 0.8125rem; +} + +.service-desc { + font-size: 0.875rem; /* text-sm */ + color: var(--zen-text-muted); + line-height: 1.6; + margin: 0 0 0 1.25rem; + max-width: 540px; +} + +/* 3. COLUMNA DERECHA (Tarjeta Formulario Zen Dark) */ +.contact-card-col { + width: 100%; +} + +.contact-card { + background-color: var(--zen-bg-surface); /* #131720 Normalizado */ + border-radius: 1.5rem; /* rounded-3xl */ + border: 1px solid var(--zen-border-subtle); /* #1e2533 Normalizado */ + padding: clamp(2rem, 4vw, 3rem); /* p-8 sm:p-12 */ + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.75); +} + +.form-header { + margin-bottom: 2rem; /* mb-8 */ +} + +.form-title { + font-family: var(--font-sans); + font-size: 1.5rem; /* text-2xl */ + font-weight: 700; /* font-semibold */ + color: var(--zen-text-primary); + margin: 0 0 0.5rem 0; /* mb-2 */ +} + +.form-subtitle { + font-size: 0.875rem; /* text-sm */ + color: var(--zen-text-secondary); + margin: 0; +} + +/* Campos del Formulario Normalizados */ +.contact-form { + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 0.4rem; +} + +.form-row-2col { + display: grid; + grid-template-columns: 1fr; + gap: 1rem; +} + +@media (min-width: 640px) { + .form-row-2col { + grid-template-columns: 1fr 1fr; /* sm:grid-cols-2 */ + } +} + +.form-label { + font-family: var(--font-sans); + font-size: 0.8125rem; /* text-sm */ + color: var(--zen-text-secondary); + font-weight: 600; + letter-spacing: 0.02em; + display: block; +} + +.form-input, +.form-select, +.form-textarea { + width: 100%; + background-color: var(--zen-bg-base); /* #0c0e14 Normalizado */ + color: var(--zen-text-primary); + padding: 0.875rem 1rem; /* p-3 o p-4 */ + border-radius: 0.75rem; /* rounded-xl */ + border: 1px solid var(--zen-border-subtle); /* #1e2533 Normalizado */ + font-family: inherit; + font-size: 0.875rem; + line-height: 1.5; + transition: border-color var(--transition-fast), box-shadow var(--transition-fast); + box-sizing: border-box; +} + +.form-input::placeholder, +.form-textarea::placeholder { + color: var(--zen-text-muted); +} + +.form-input:focus, +.form-select:focus, +.form-textarea:focus { + outline: none; + border-color: var(--zen-accent-kintsugi); + box-shadow: 0 0 0 2px rgba(197, 155, 39, 0.25); +} + +.form-textarea { + resize: vertical; + min-height: 110px; +} + +.select-wrapper { + position: relative; +} + +.form-select { + appearance: none; + cursor: pointer; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%239aa5b8'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 1rem center; + background-size: 1.25rem; + padding-right: 2.5rem; +} + +.form-select option { + background-color: var(--zen-bg-surface); + color: var(--zen-text-primary); +} + +.input-error { + border-color: #f7768e !important; +} + +.field-error { + font-size: 0.6875rem; + color: #f7768e; +} + +.error-banner { + padding: 0.75rem 1rem; + background-color: rgba(247, 118, 142, 0.1); + border: 1px solid rgba(247, 118, 142, 0.3); + border-radius: 0.5rem; + color: #f7768e; + font-size: 0.8125rem; +} + +/* Botón de Envío Kintsugi Gold */ +.submit-btn { + width: 100%; /* w-full */ + background: linear-gradient(135deg, var(--zen-accent-kintsugi) 0%, #e2b94a 100%); + color: #07080b; /* text-black de alto contraste */ + font-weight: 700; /* font-semibold */ + padding: 1rem; /* py-4 */ + border-radius: 0.75rem; /* rounded-xl */ + border: none; + font-size: 0.9375rem; + cursor: pointer; + box-shadow: 0 4px 16px rgba(197, 155, 39, 0.3); + transition: transform var(--transition-fast), box-shadow var(--transition-fast); + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + margin-top: 0.5rem; +} + +.submit-btn:hover:not(:disabled) { + box-shadow: 0 0 24px rgba(197, 155, 39, 0.55); + transform: translateY(-2px); +} + +.submit-btn:active:not(:disabled) { + transform: scale(0.99); +} + +.submit-btn:disabled { + opacity: 0.7; + cursor: not-allowed; +} + +.spinner { + width: 16px; + height: 16px; + border: 2px solid rgba(0, 0, 0, 0.2); + border-top-color: #000000; + border-radius: 50%; + animation: spin 600ms linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Estado de Éxito */ +.success-banner { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 2.5rem 1rem; + gap: 1rem; +} + +.success-icon-badge { + width: 52px; + height: 52px; + border-radius: 50%; + background-color: rgba(197, 155, 39, 0.15); + border: 1px solid rgba(197, 155, 39, 0.35); + color: var(--zen-accent-kintsugi); + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; + font-weight: bold; +} + +.success-title { + font-size: 1.25rem; + font-weight: 700; + color: var(--zen-text-primary); + margin: 0; +} + +.success-desc { + font-size: 0.875rem; + color: var(--zen-text-secondary); + line-height: 1.6; + margin: 0; + max-width: 420px; +} + +.btn-new-message { + margin-top: 0.5rem; + background: rgba(255, 255, 255, 0.06); + border: 1px solid var(--zen-border-subtle); + color: var(--zen-text-primary); + padding: 0.625rem 1.25rem; + border-radius: 0.5rem; + font-size: 0.8125rem; + cursor: pointer; + transition: background-color var(--transition-fast); +} + +.btn-new-message:hover { + background: rgba(255, 255, 255, 0.12); +} diff --git a/src/frontend/src/app/presentation/features/contact-section/contact-section.component.html b/src/frontend/src/app/presentation/features/contact-section/contact-section.component.html new file mode 100644 index 0000000..cc6ca1e --- /dev/null +++ b/src/frontend/src/app/presentation/features/contact-section/contact-section.component.html @@ -0,0 +1,184 @@ +
+
+
+ +
+ + + {{ i18n.t().contactEyebrow }} + + + +

+ {{ i18n.t().contactTitle }} +

+ + +

+ {{ i18n.t().contactLead }} +

+ + +
+

{{ i18n.t().contactService1Title }}

+

+ {{ i18n.t().contactService1Desc }} +

+
+ + +
+

{{ i18n.t().contactService2Title }}

+

+ {{ i18n.t().contactService2Desc }} +

+
+
+ + +
+
+ +
+

{{ i18n.t().contactFormTitle }}

+

+ {{ i18n.t().contactFormSubtitle }} +

+
+ + @if (isSuccess()) { + + + } @else { + +
+ +
+ + + @if ( + contactForm.controls.fullNameOrCompany.invalid && + contactForm.controls.fullNameOrCompany.touched + ) { + {{ i18n.t().contactErrorName }} + } +
+ + +
+
+ + + @if ( + contactForm.controls.email.invalid && contactForm.controls.email.touched + ) { + {{ i18n.t().contactErrorEmail }} + } +
+ +
+ + +
+
+ + +
+ +
+ +
+
+ + +
+ + + @if ( + contactForm.controls.projectDetails.invalid && + contactForm.controls.projectDetails.touched + ) { + {{ i18n.t().contactErrorDetails }} + } +
+ + + @if (errorMessage()) { + + } + + + +
+ } +
+
+
+
+
diff --git a/src/frontend/src/app/presentation/features/contact-section/contact-section.component.ts b/src/frontend/src/app/presentation/features/contact-section/contact-section.component.ts new file mode 100644 index 0000000..7beea47 --- /dev/null +++ b/src/frontend/src/app/presentation/features/contact-section/contact-section.component.ts @@ -0,0 +1,70 @@ +import { Component, ChangeDetectionStrategy, signal, inject } from '@angular/core'; +import { + ReactiveFormsModule, + FormGroup, + FormControl, + Validators +} from '@angular/forms'; +import { I18nService } from '../../../application/services/i18n.service'; + +@Component({ + selector: 'app-contact-section', + standalone: true, + imports: [ReactiveFormsModule], + templateUrl: './contact-section.component.html', + styleUrl: './contact-section.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ContactSectionComponent { + protected readonly i18n = inject(I18nService); + + // Estado reactivo del formulario con Signals + protected readonly isSubmitting = signal(false); + protected readonly isSuccess = signal(false); + protected readonly errorMessage = signal(null); + + // Definición del FormGroup reactivo tipado + protected readonly contactForm = new FormGroup({ + fullNameOrCompany: new FormControl('', { + nonNullable: true, + validators: [Validators.required, Validators.minLength(3)] + }), + email: new FormControl('', { + nonNullable: true, + validators: [Validators.required, Validators.email] + }), + phone: new FormControl('', { + nonNullable: true + }), + serviceType: new FormControl('', { + nonNullable: true + }), + projectDetails: new FormControl('', { + nonNullable: true, + validators: [Validators.required, Validators.minLength(10)] + }) + }); + + onSubmit(): void { + if (this.contactForm.invalid) { + this.contactForm.markAllAsTouched(); + this.errorMessage.set(this.i18n.t().contactFormError); + return; + } + + this.errorMessage.set(null); + this.isSubmitting.set(true); + + // Simulación de envío con confirmación visual inmediata + setTimeout(() => { + this.isSubmitting.set(false); + this.isSuccess.set(true); + }, 600); + } + + resetForm(): void { + this.contactForm.reset(); + this.isSuccess.set(false); + this.errorMessage.set(null); + } +} diff --git a/src/frontend/src/app/presentation/features/featured-projects/featured-projects.component.css b/src/frontend/src/app/presentation/features/featured-projects/featured-projects.component.css new file mode 100644 index 0000000..7586115 --- /dev/null +++ b/src/frontend/src/app/presentation/features/featured-projects/featured-projects.component.css @@ -0,0 +1,236 @@ +:host { + display: block; + width: 100%; + height: 100%; +} + +.featured-projects-wrapper { + display: flex; + flex-direction: column; + width: 100%; + gap: var(--space-4); + user-select: text; +} + +/* Micro-Cabecera de la sección */ +.section-micro-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-2) var(--space-4); + background: rgba(18, 22, 28, 0.6); + border: 1px solid var(--zen-border-subtle); + border-radius: var(--zen-radius-xs); +} + +.header-left { + display: flex; + align-items: center; + gap: var(--space-2); +} + +.pulse-indicator { + width: 7px; + height: 7px; + border-radius: 50%; + background-color: var(--zen-accent-kintsugi); + box-shadow: 0 0 8px var(--zen-accent-kintsugi); +} + +.header-tag { + font-family: var(--font-sans); + font-size: 0.8125rem; + font-weight: 700; + color: var(--zen-text-primary); + letter-spacing: 0.08em; +} + +.header-count { + font-family: var(--font-sans); + font-size: 0.75rem; + color: var(--zen-text-muted); +} + +/* Grid de 3 columnas para los proyectos */ +.projects-stack { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-4); +} + +@media (min-width: 960px) { + .projects-stack { + grid-template-columns: repeat(3, 1fr); + gap: var(--space-5); + } +} + +/* Tarjeta individual de proyecto */ +.project-card { + display: flex; + flex-direction: column; + align-items: stretch; + gap: var(--space-3); + padding: var(--space-4); + background-color: rgba(14, 18, 24, 0.75); + border: 1px solid var(--zen-border-subtle); + border-top: 3px solid var(--prj-accent, var(--zen-accent-kintsugi)); + border-radius: var(--zen-radius-xs); + transition: transform var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast), background-color var(--transition-fast); + cursor: default; +} + +.project-card:hover { + background-color: rgba(18, 24, 32, 0.95); + border-color: var(--prj-accent, var(--zen-accent-kintsugi)); + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6), 0 0 16px rgba(125, 207, 255, 0.05); + transform: translateY(-4px); +} + +/* Espacio reservado para la imagen del proyecto (Banner superior de la card) */ +.project-image-slot { + width: 100%; + height: 150px; + background-color: #090b0e; + border: 1px solid var(--zen-border-subtle); + border-radius: var(--zen-radius-xs); + overflow: hidden; + position: relative; + display: flex; + align-items: center; + justify-content: center; +} + +.project-img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +/* Marcador de posición técnico cuando aún no hay archivo subido */ +.image-placeholder { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 3px; + width: 100%; + height: 100%; + background: radial-gradient(circle at center, rgba(255, 255, 255, 0.03) 0%, transparent 80%); + border: 1px dashed rgba(255, 255, 255, 0.12); + color: var(--zen-text-muted); + user-select: none; +} + +.placeholder-icon { + font-size: 1.25rem; + color: var(--prj-accent, var(--zen-accent-kintsugi)); + opacity: 0.8; +} + +.placeholder-code { + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 700; + color: var(--zen-text-secondary); +} + +.placeholder-label { + font-family: var(--font-sans); + font-size: 0.6875rem; + color: var(--zen-text-muted); + letter-spacing: 0.05em; +} + +/* Contenido informativo del proyecto */ +.project-content { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 3px; +} + +.project-meta-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-2); +} + +.institution-badge { + display: flex; + align-items: center; + gap: 5px; +} + +.meta-bullet { + font-size: 0.5rem; + color: var(--prj-accent, var(--zen-accent-kintsugi)); +} + +.meta-institution { + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; + color: var(--zen-text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.meta-tag { + font-family: var(--font-sans); + font-size: 0.6875rem; + color: var(--zen-text-muted); + white-space: nowrap; +} + +.project-title { + font-family: var(--font-sans); + font-size: 1.25rem; + font-weight: 800; + color: var(--zen-text-primary); + margin: 0; + line-height: 1.25; + letter-spacing: -0.01em; +} + +.project-impact { + font-family: var(--font-sans); + font-size: 0.8125rem; + color: var(--zen-text-secondary); + line-height: 1.5; + margin: 0; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +/* Fila de chips tecnológicos */ +.project-tags-row { + display: flex; + flex-wrap: wrap; + gap: 4px; + margin-top: 2px; +} + +.tech-chip { + font-family: var(--font-sans); + font-size: 0.6875rem; + font-weight: 600; + padding: 2px 7px; + background-color: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 2px; + color: var(--zen-text-muted); + transition: color var(--transition-fast), border-color var(--transition-fast); +} + +.project-card:hover .tech-chip { + color: var(--zen-text-primary); + border-color: rgba(255, 255, 255, 0.18); +} diff --git a/src/frontend/src/app/presentation/features/featured-projects/featured-projects.component.html b/src/frontend/src/app/presentation/features/featured-projects/featured-projects.component.html new file mode 100644 index 0000000..8a140bc --- /dev/null +++ b/src/frontend/src/app/presentation/features/featured-projects/featured-projects.component.html @@ -0,0 +1,55 @@ + \ No newline at end of file diff --git a/src/frontend/src/app/presentation/features/featured-projects/featured-projects.component.ts b/src/frontend/src/app/presentation/features/featured-projects/featured-projects.component.ts new file mode 100644 index 0000000..47f192a --- /dev/null +++ b/src/frontend/src/app/presentation/features/featured-projects/featured-projects.component.ts @@ -0,0 +1,76 @@ +import { Component, ChangeDetectionStrategy, signal, computed, inject } from '@angular/core'; +import { I18nService } from '../../../application/services/i18n.service'; + +export interface ProjectCardData { + readonly id: string; + readonly code: string; + readonly title: string; + readonly subtitle: string; + readonly institution: string; + readonly description: string; + readonly impact: string; + readonly tags: readonly string[]; + readonly imageUrl?: string; + readonly accentColor: string; +} + +@Component({ + selector: 'app-featured-projects', + standalone: true, + templateUrl: './featured-projects.component.html', + styleUrl: './featured-projects.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class FeaturedProjectsComponent { + protected readonly i18n = inject(I18nService); + + // Proyectos extraídos directamente de la hoja de vida (CV) de Bryan Baño con soporte i18n + protected readonly projects = computed(() => { + const t = this.i18n.t(); + return [ + { + id: 'siplece', + code: 'PRJ-01', + title: 'SIPLECE', + subtitle: t.prj1Subtitle, + institution: 'IST Mayor Pedro Traversari', + description: t.prj1Desc, + impact: t.prj1Impact, + tags: ['Angular', '.NET 8', 'MySQL', 'JWT', 'SharePoint'], + imageUrl: 'assets/images/projects/siplece.webp', + accentColor: '#7dcfff' + }, + { + id: 'vita', + code: 'PRJ-02', + title: 'VITA Automotriz', + subtitle: t.prj2Subtitle, + institution: t.prj2Institution, + description: t.prj2Desc, + impact: t.prj2Impact, + tags: ['Angular', '.NET 8', 'MySQL', 'REST API'], + imageUrl: 'assets/images/projects/vita.webp', + accentColor: '#9ece6a' + }, + { + id: 'ammi', + code: 'PRJ-03', + title: 'Ammi-Online', + subtitle: t.prj3Subtitle, + institution: t.prj3Institution, + description: t.prj3Desc, + impact: t.prj3Impact, + tags: ['Angular', '.NET 8', 'MySQL', 'Clean Arch'], + imageUrl: 'assets/images/projects/ammi.webp', + accentColor: '#bb9af7' + } + ]; + }); + + // Manejo de fallback elegante si el usuario aún no ha colocado los archivos de imagen + protected readonly imageErrors = signal>({}); + + onImageError(projectId: string): void { + this.imageErrors.update(curr => ({ ...curr, [projectId]: true })); + } +} diff --git a/src/frontend/src/app/presentation/features/hero-3d/hero-3d.component.css b/src/frontend/src/app/presentation/features/hero-3d/hero-3d.component.css new file mode 100644 index 0000000..62e1492 --- /dev/null +++ b/src/frontend/src/app/presentation/features/hero-3d/hero-3d.component.css @@ -0,0 +1,98 @@ +:host { display: block; width: 100%; } +.hero-scroll-wrapper { position: relative; width: 100%; height: 400vh; } +.hero-sticky-stage { + position: sticky; top: 0; width: 100%; height: 100vh; overflow: hidden; + background: #000; display: flex; align-items: center; justify-content: center; +} +.thinkpad-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; z-index: 1; } + +.zen-loader-pill { + position: absolute; top: calc(var(--space-8) + 36px); right: clamp(1rem, 3vw, 2.5rem); + display: flex; align-items: center; gap: 8px; + background: rgba(12, 14, 20, 0.85); backdrop-filter: blur(8px); + border: 1px solid var(--zen-border-subtle); padding: 4px 12px; + border-radius: 2px; font-family: var(--font-sans); font-size: 0.75rem; + font-weight: 600; color: var(--zen-accent-kintsugi); z-index: 30; +} +.loader-dot { + width: 6px; height: 6px; border-radius: 50%; background: var(--zen-accent-kintsugi); + animation: pulse-l 1.2s infinite ease-in-out; +} +@keyframes pulse-l { + 0%, 100% { opacity: 0.4; transform: scale(0.85); } + 50% { opacity: 1; transform: scale(1.15); } +} + +.lateral-panel { + position: absolute; z-index: 10; width: 100%; opacity: 0; visibility: hidden; pointer-events: none; + transition: opacity 0.4s ease, transform 0.4s ease, visibility 0.4s; +} +.side-left { left: clamp(1.5rem, 4vw, 4rem); transform: translateY(calc(-50% + 16px)); } +.side-right { right: clamp(1.5rem, 4vw, 4rem); transform: translateY(calc(-50% + 16px)); } +.lateral-panel.active { opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(-50%); } + +.pos-f1-left { top: 45%; max-width: 440px; } +.pos-f1-right { top: 58%; max-width: 275px; } +.pos-f2-left { top: 54%; max-width: 370px; } +.pos-f2-right { top: 40%; max-width: 330px; } +.pos-f3-left { top: 42%; max-width: 300px; } +.pos-f3-right { top: 52%; max-width: 380px; } + +/* Tarjeta Insignia Principal de Bryan */ +.hero-lead-card, .panel-card { + border: 1px solid var(--zen-border-subtle); border-radius: 2px; + backdrop-filter: blur(12px); +} +.hero-lead-card { + background: rgba(11, 14, 20, 0.9); + border-color: rgba(197, 155, 39, 0.45); + border-left: 4px solid var(--zen-accent-kintsugi); + padding: 24px; + box-shadow: 0 0 35px rgba(197, 155, 39, 0.18), 0 20px 50px rgba(0, 0, 0, 0.9); +} +.lead-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } +.lead-status { font-family: var(--font-sans); font-size: 0.75rem; color: var(--zen-accent-matcha); font-weight: 700; } +.lead-tag { font-family: var(--font-sans); font-size: 0.75rem; color: var(--zen-text-muted); font-weight: 600; } +.lead-name { + font-family: var(--font-sans); + font-size: clamp(2rem, 3vw, 2.5rem); font-weight: 800; color: var(--zen-text-primary); + line-height: 1.1; letter-spacing: -0.02em; margin-bottom: 2px; +} +.lead-role, .panel-eyebrow { + display: block; font-family: var(--font-sans); font-size: 0.8125rem; font-weight: 700; + letter-spacing: 0.06em; text-transform: uppercase; + color: var(--zen-accent-kintsugi); margin-bottom: 8px; +} +.lead-desc { font-family: var(--font-sans); font-size: 0.9375rem; color: var(--zen-text-secondary); line-height: 1.6; margin-bottom: 16px; } +.lead-badges, .panel-tags { display: flex; flex-wrap: wrap; gap: 8px; } +.lead-footer { border-top: 1px solid var(--zen-border-subtle); padding-top: 10px; margin-top: 14px; } +.lead-metric { font-family: var(--font-sans); font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em; color: var(--zen-text-muted); } + +/* Tarjetas Secundarias */ +.panel-card { + background: rgba(10, 12, 16, 0.82); + border-left: 3px solid var(--zen-accent-kintsugi); + padding: 18px; + box-shadow: 0 12px 36px rgba(0, 0, 0, 0.85); +} +.compact-card { padding: 14px; } +.panel-title { font-family: var(--font-sans); font-size: 1.3rem; font-weight: 800; line-height: 1.25; color: var(--zen-text-primary); margin-bottom: 6px; } +.panel-desc { font-family: var(--font-sans); font-size: 0.875rem; color: var(--zen-text-secondary); line-height: 1.55; margin-bottom: 12px; } + +/* Chips de Etiquetas */ +.zen-chip { + font-family: var(--font-sans); font-size: 0.75rem; font-weight: 600; padding: 3px 9px; border-radius: 2px; + background: rgba(255, 255, 255, 0.04); border: 1px solid var(--zen-border-subtle); color: var(--zen-text-primary); +} +.chip-gold { border-color: rgba(197, 155, 39, 0.45); color: var(--zen-accent-kintsugi); background: rgba(197, 155, 39, 0.08); } +.chip-matcha { border-color: rgba(78, 155, 111, 0.45); color: var(--zen-accent-matcha); background: rgba(78, 155, 111, 0.08); } + +@media (max-width: 900px) { + .lateral-panel { + max-width: min(90%, 380px) !important; left: 50% !important; right: auto !important; + transform: translateX(-50%) translateY(16px); + } + .side-left { top: 12% !important; } + .side-right { top: auto !important; bottom: 8% !important; } + .lateral-panel.active { transform: translateX(-50%) translateY(0); } +} diff --git a/src/frontend/src/app/presentation/features/hero-3d/hero-3d.component.html b/src/frontend/src/app/presentation/features/hero-3d/hero-3d.component.html new file mode 100644 index 0000000..c1e89f1 --- /dev/null +++ b/src/frontend/src/app/presentation/features/hero-3d/hero-3d.component.html @@ -0,0 +1,96 @@ +
+
+ + + + + @if (!isLoaded()) { +
+ + {{ i18n.t().preloading }} // {{ loadProgress() }}% +
+ } + + + + + + + + + + + + + + + +
+
diff --git a/src/frontend/src/app/presentation/features/hero-3d/hero-3d.component.ts b/src/frontend/src/app/presentation/features/hero-3d/hero-3d.component.ts new file mode 100644 index 0000000..15286ab --- /dev/null +++ b/src/frontend/src/app/presentation/features/hero-3d/hero-3d.component.ts @@ -0,0 +1,279 @@ +import { + Component, + OnInit, + AfterViewInit, + OnDestroy, + ViewChild, + ElementRef, + ChangeDetectionStrategy, + signal, + inject, + NgZone +} from '@angular/core'; +import { I18nService } from '../../../application/services/i18n.service'; + +const TOTAL_FRAMES = 240; +const LERP_FACTOR = 0.09; + +@Component({ + selector: 'app-hero-3d', + standalone: true, + templateUrl: './hero-3d.component.html', + styleUrl: './hero-3d.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Hero3dComponent implements OnInit, AfterViewInit, OnDestroy { + @ViewChild('scrollContainer') private containerRef!: ElementRef; + @ViewChild('canvas') private canvasRef!: ElementRef; + + protected readonly i18n = inject(I18nService); + private readonly ngZone = inject(NgZone); + + // Señales reactivas: Fase activa de tarjetas laterales y estado de carga + protected readonly activePhase = signal(1); + protected readonly loadProgress = signal(0); + protected readonly isLoaded = signal(false); + + private ctx: CanvasRenderingContext2D | null = null; + private readonly images: HTMLImageElement[] = new Array(TOTAL_FRAMES + 1); + private targetFrame = 1; + private currentInterpolatedFrame = 1; + private lastRenderedFrame = 0; + private isVisible = true; + private isReducedMotion = false; + private isLoopRunning = false; + private animationFrameId: number | null = null; + private intersectionObserver?: IntersectionObserver; + private mediaQueryList?: MediaQueryList; + + ngOnInit(): void { + this.checkReducedMotion(); + this.preloadImages(); + } + + ngAfterViewInit(): void { + const canvas = this.canvasRef.nativeElement; + this.ctx = canvas.getContext('2d', { alpha: false }); + + this.intersectionObserver = new IntersectionObserver(([entry]) => { + this.isVisible = entry.isIntersecting; + if (this.isVisible && !this.isReducedMotion) { + this.startAnimationLoop(); + } else if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); + this.isLoopRunning = false; + } + }, { threshold: 0.02 }); + + this.intersectionObserver.observe(this.containerRef.nativeElement); + + this.ngZone.runOutsideAngular(() => { + window.addEventListener('scroll', this.onScroll, { passive: true }); + window.addEventListener('resize', this.onResize, { passive: true }); + }); + + this.onResize(); + this.onScroll(); + } + + private checkReducedMotion(): void { + this.mediaQueryList = window.matchMedia('(prefers-reduced-motion: reduce)'); + this.isReducedMotion = this.mediaQueryList.matches; + + this.mediaQueryList.addEventListener('change', (e) => { + this.isReducedMotion = e.matches; + if (this.isReducedMotion) { + if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); + this.isLoopRunning = false; + } + this.renderFrame(1); + } else { + this.startAnimationLoop(); + } + }); + } + + private preloadImages(): void { + let loaded = 0; + + const firstImg = new Image(); + firstImg.src = this.getFramePath(1); + firstImg.onload = () => { + this.images[1] = firstImg; + loaded++; + this.renderFrame(1); + + for (let i = 2; i <= TOTAL_FRAMES; i++) { + const img = new Image(); + img.src = this.getFramePath(i); + img.onload = () => { + this.images[i] = img; + loaded++; + if (loaded % 12 === 0 || loaded === TOTAL_FRAMES) { + const pct = Math.round((loaded / TOTAL_FRAMES) * 100); + this.loadProgress.set(pct); + if (loaded === TOTAL_FRAMES) { + this.isLoaded.set(true); + } + } + }; + img.onerror = () => { + loaded++; + }; + } + }; + } + + private getFramePath(index: number): string { + const padded = String(index).padStart(3, '0'); + return `/assets/images/thinkpad-sequence/ezgif-frame-${padded}.jpg`; + } + + private onScroll = (): void => { + if (!this.isVisible || !this.containerRef || this.isReducedMotion) return; + + const container = this.containerRef.nativeElement; + const rect = container.getBoundingClientRect(); + const scrollableDistance = container.offsetHeight - window.innerHeight; + + if (scrollableDistance <= 0) return; + + const rawProgress = -rect.top / scrollableDistance; + const progress = Math.min(Math.max(rawProgress, 0), 1); + this.targetFrame = Math.min(Math.max(1, 1 + progress * (TOTAL_FRAMES - 1)), TOTAL_FRAMES); + + this.startAnimationLoop(); + }; + + private onResize = (): void => { + this.resizeCanvas(); + this.renderFrame(Math.round(this.currentInterpolatedFrame)); + }; + + private startAnimationLoop(): void { + if (this.isLoopRunning || !this.isVisible || this.isReducedMotion) return; + this.isLoopRunning = true; + + this.ngZone.runOutsideAngular(() => { + const loop = () => { + if (!this.isVisible || this.isReducedMotion) { + this.isLoopRunning = false; + return; + } + + const diff = this.targetFrame - this.currentInterpolatedFrame; + if (Math.abs(diff) > 0.005) { + this.currentInterpolatedFrame += diff * LERP_FACTOR; + } else { + this.currentInterpolatedFrame = this.targetFrame; + } + + const frameToDraw = Math.round(this.currentInterpolatedFrame); + + if (frameToDraw !== this.lastRenderedFrame) { + this.renderFrame(frameToDraw); + this.lastRenderedFrame = frameToDraw; + this.updateActivePhase(this.currentInterpolatedFrame); + } + + if (Math.abs(this.targetFrame - this.currentInterpolatedFrame) > 0.01) { + this.animationFrameId = requestAnimationFrame(loop); + } else { + this.isLoopRunning = false; + } + }; + + this.animationFrameId = requestAnimationFrame(loop); + }); + } + + private updateActivePhase(currentFrame: number): void { + const progress = (currentFrame - 1) / (TOTAL_FRAMES - 1); + let phase = 1; + + if (progress < 0.32) { + phase = 1; + } else if (progress < 0.65) { + phase = 2; + } else if (progress < 0.94) { + phase = 3; + } else { + phase = 0; // Desvanecimiento completo al final del scroll + } + + if (this.activePhase() !== phase) { + this.activePhase.set(phase); + } + } + + private resizeCanvas(): void { + if (!this.canvasRef || !this.ctx) return; + const canvas = this.canvasRef.nativeElement; + const dpr = Math.min(window.devicePixelRatio || 1, 2); + const targetW = Math.round(window.innerWidth * dpr); + const targetH = Math.round(window.innerHeight * dpr); + + if (canvas.width !== targetW || canvas.height !== targetH) { + canvas.width = targetW; + canvas.height = targetH; + } + } + + private getBestFrame(targetIndex: number): HTMLImageElement | null { + const rounded = Math.min(Math.max(1, Math.round(targetIndex)), TOTAL_FRAMES); + if (this.images[rounded]?.complete) return this.images[rounded]; + + for (let offset = 1; offset < TOTAL_FRAMES; offset++) { + const prev = rounded - offset; + if (prev >= 1 && this.images[prev]?.complete) return this.images[prev]; + const next = rounded + offset; + if (next <= TOTAL_FRAMES && this.images[next]?.complete) return this.images[next]; + } + return this.images[1]?.complete ? this.images[1] : null; + } + + private renderFrame(targetIndex: number): void { + if (!this.ctx || !this.canvasRef) return; + this.resizeCanvas(); + + const img = this.getBestFrame(targetIndex); + if (!img || !img.complete) return; + + const canvas = this.canvasRef.nativeElement; + const ctx = this.ctx; + + ctx.fillStyle = '#000000'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + const imgAspect = 16 / 9; + const screenAspect = canvas.width / canvas.height; + let drawW: number; + let drawH: number; + let offsetX = 0; + let offsetY = 0; + + if (screenAspect > imgAspect) { + drawW = canvas.width; + drawH = canvas.width / imgAspect; + offsetY = (canvas.height - drawH) / 2; + } else { + drawH = canvas.height; + drawW = canvas.height * imgAspect; + offsetX = (canvas.width - drawW) / 2; + } + + ctx.imageSmoothingEnabled = true; + ctx.imageSmoothingQuality = 'high'; + ctx.drawImage(img, offsetX, offsetY, drawW, drawH); + } + + ngOnDestroy(): void { + window.removeEventListener('scroll', this.onScroll); + window.removeEventListener('resize', this.onResize); + this.intersectionObserver?.disconnect(); + if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); + } + } +} diff --git a/src/frontend/src/app/presentation/features/landing/landing.component.css b/src/frontend/src/app/presentation/features/landing/landing.component.css new file mode 100644 index 0000000..3f84af0 --- /dev/null +++ b/src/frontend/src/app/presentation/features/landing/landing.component.css @@ -0,0 +1,244 @@ +:host { + display: block; + width: 100%; + min-height: 100vh; + background-color: var(--zen-bg-base); + color: var(--zen-text-primary); +} + +.landing-container { + display: flex; + flex-direction: column; + min-height: 100vh; + position: relative; + background-color: var(--zen-bg-base); +} + +/* Header Minimalista Flotante */ +.zen-header { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: space-between; + height: 56px; + padding: 0 clamp(1rem, 3vw, 2.5rem); + background: rgba(10, 10, 11, 0.75); + backdrop-filter: blur(12px) saturate(160%); + border-bottom: 1px solid var(--zen-border-subtle); +} + +.brand-block { + display: flex; + align-items: center; + gap: var(--space-2); + font-family: var(--font-sans); + font-size: 0.875rem; + letter-spacing: 0.04em; +} + +.brand-monogram { + font-weight: 800; + color: var(--zen-accent-matcha); + background: rgba(158, 206, 106, 0.12); + padding: 2px 6px; + border-radius: var(--zen-radius-xs); + border: 1px solid rgba(158, 206, 106, 0.25); +} + +.brand-separator { color: var(--zen-border-subtle); } +.brand-name { font-weight: 700; color: var(--zen-text-primary); } + +.header-right-group { + display: flex; + align-items: center; + gap: var(--space-4); +} + +.time-location-block { + display: flex; + flex-direction: column; + align-items: flex-end; + justify-content: center; + font-family: var(--font-sans); + line-height: 1.25; +} + +.live-clock { + font-size: 0.875rem; + font-weight: 600; + color: var(--zen-text-primary); + letter-spacing: 0.02em; +} + +.location-label { + font-size: 0.75rem; + color: var(--zen-text-muted); +} + +.lang-toggle-btn { + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 700; + color: var(--zen-text-primary); + background: var(--zen-bg-surface); + border: 1px solid var(--zen-border-subtle); + border-radius: var(--zen-radius-xs); + padding: 4px 10px; + cursor: pointer; + transition: var(--transition-fast); +} + +.lang-toggle-btn:hover { + border-color: var(--zen-accent-kintsugi); + color: var(--zen-accent-kintsugi); + background: var(--zen-bg-elevated); +} + +.hero-container { width: 100%; } + +/* ============================================================================== + MÓDULOS INTERACTIVOS (50/50 HORIZONTAL) + ============================================================================== */ + +.interactive-stage { + width: 100%; + padding: var(--space-12) clamp(1rem, 3vw, 2.5rem) var(--space-16) clamp(1rem, 3vw, 2.5rem); + background-color: #121517; + background: linear-gradient(180deg, #000000 0%, #121517 var(--space-12)); +} + +.stage-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--space-8); + width: 100%; + align-items: stretch; +} + +@media (max-width: 960px) { + .stage-grid { + grid-template-columns: 1fr; + } +} + +.module-card { + background: var(--zen-bg-surface); + border: 1px solid var(--zen-border-subtle); + border-radius: var(--zen-radius-sm); + overflow: hidden; + box-shadow: var(--zen-shadow-sm); +} + +.retro-module-card { + background: #121517; + border: none; + box-shadow: none; +} + +.profile-module-card { + background: transparent; + border: none; + box-shadow: none; + height: 100%; + display: flex; + flex-direction: column; +} + +/* ============================================================================== + SECCIÓN DE PROYECTOS DESTACADOS (CASOS REALES A ANCHO COMPLETO) + ============================================================================== */ + +.projects-stage { + width: 100%; + padding: var(--space-16) clamp(1rem, 3vw, 2.5rem); + background-color: #121517; + border-top: 1px solid rgba(255, 255, 255, 0.04); +} + +.projects-stage-inner { + width: 100%; +} + +/* ============================================================================== + SECCIÓN DEDICADA: TERMINAL ARCH LINUX (AMPLIA Y CONFORTABLE) + ============================================================================== */ + +.terminal-stage { + width: 100%; + padding: var(--space-16) clamp(1rem, 3vw, 2.5rem); + background-color: #121517; + border-top: 1px solid rgba(255, 255, 255, 0.04); +} + +.terminal-stage-inner { + max-width: 1040px; + margin: 0 auto; + width: 100%; + display: flex; + flex-direction: column; + gap: var(--space-4); +} + +.terminal-header-meta { + display: flex; + flex-direction: column; + gap: 4px; +} + +.mono-section-tag { + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 700; + color: var(--zen-accent-kintsugi); + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.terminal-section-heading { + font-family: var(--font-sans); + font-size: clamp(1.75rem, 3vw, 2.25rem); + font-weight: 800; + color: var(--zen-text-primary); + margin: 0; + letter-spacing: -0.02em; +} + +.terminal-section-desc { + font-family: var(--font-sans); + font-size: 0.9375rem; + color: var(--zen-text-secondary); + margin: 0; + max-width: 680px; + line-height: 1.6; +} + +/* Footer Minimalista */ +.zen-footer { + margin-top: auto; + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-6) clamp(1rem, 3vw, 2.5rem); + border-top: 1px solid var(--zen-border-subtle); + background-color: #121517; +} + +.footer-copy { + font-family: var(--font-sans); + font-size: 0.8125rem; + color: var(--zen-text-muted); +} + +.footer-version { + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; + color: var(--zen-accent-matcha); + background: rgba(158, 206, 106, 0.08); + padding: 2px 6px; + border-radius: var(--zen-radius-xs); +} diff --git a/src/frontend/src/app/presentation/features/landing/landing.component.html b/src/frontend/src/app/presentation/features/landing/landing.component.html new file mode 100644 index 0000000..8f1dd0f --- /dev/null +++ b/src/frontend/src/app/presentation/features/landing/landing.component.html @@ -0,0 +1,72 @@ +
+ +
+
+ BRYAN + / + BAÑO +
+ +
+
+ {{ currentTime() }} + {{ i18n.t().location }} +
+ + +
+
+ + +
+ +
+ + +
+
+ +
+ +
+ + +
+ +
+
+
+ + +
+
+ +
+
+ + +
+
+
+ +

{{ i18n.t().terminalHeading }}

+

+ {{ i18n.t().terminalSectionDesc }} +

+
+ +
+
+ + + + + +
+ + v1.0.0-alpha +
+
\ No newline at end of file diff --git a/src/frontend/src/app/presentation/features/landing/landing.component.ts b/src/frontend/src/app/presentation/features/landing/landing.component.ts new file mode 100644 index 0000000..a96579d --- /dev/null +++ b/src/frontend/src/app/presentation/features/landing/landing.component.ts @@ -0,0 +1,53 @@ +import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, signal, inject } from '@angular/core'; +import { Hero3dComponent } from '../hero-3d/hero-3d.component'; +import { RetroOsComponent } from '../retro-os/retro-os.component'; +import { TerminalComponent } from '../terminal/terminal.component'; +import { FeaturedProjectsComponent } from '../featured-projects/featured-projects.component'; +import { ContactSectionComponent } from '../contact-section/contact-section.component'; +import { TechnicalProfileComponent } from '../technical-profile/technical-profile.component'; +import { I18nService } from '../../../application/services/i18n.service'; + +@Component({ + selector: 'app-landing', + standalone: true, + imports: [ + Hero3dComponent, + RetroOsComponent, + TerminalComponent, + FeaturedProjectsComponent, + ContactSectionComponent, + TechnicalProfileComponent + ], + templateUrl: './landing.component.html', + styleUrl: './landing.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class LandingComponent implements OnInit, OnDestroy { + protected readonly i18n = inject(I18nService); + protected readonly currentYear = signal(new Date().getFullYear()); + protected readonly currentTime = signal(''); + + private clockInterval: ReturnType | null = null; + private readonly timeFormatter = new Intl.DateTimeFormat('en-GB', { + timeZone: 'America/Guayaquil', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false + }); + + ngOnInit(): void { + this.updateClock(); + this.clockInterval = setInterval(() => this.updateClock(), 1000); + } + + private updateClock(): void { + this.currentTime.set(this.timeFormatter.format(new Date())); + } + + ngOnDestroy(): void { + if (this.clockInterval !== null) { + clearInterval(this.clockInterval); + } + } +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-cv/retro-cv.component.css b/src/frontend/src/app/presentation/features/retro-os/components/retro-cv/retro-cv.component.css new file mode 100644 index 0000000..9a620c1 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-cv/retro-cv.component.css @@ -0,0 +1,153 @@ +:host { + display: block; + width: 100%; + height: 100%; + overflow: hidden; +} + +.xp-pdf-container { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + background-color: #ece9d8; +} + +/* Barra de Herramientas Estilo Windows XP */ +.xp-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 4px 8px; + background: linear-gradient(180deg, #fbfbfb 0%, #ebe7d6 100%); + border-bottom: 1px solid #c0bca8; + box-shadow: inset 0 1px 0 #ffffff; + font-family: var(--font-sans); + gap: 8px; + flex-shrink: 0; +} + +.doc-info-block { + display: flex; + align-items: center; + gap: 6px; + min-width: 0; +} + +.pdf-icon-badge { + background-color: #d32f2f; + color: #ffffff; + font-size: 0.5625rem; + font-weight: 800; + padding: 1px 4px; + border-radius: 2px; + letter-spacing: 0.05em; + flex-shrink: 0; +} + +.doc-title { + font-size: 0.75rem; + font-weight: 700; + color: #212529; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.doc-size { + font-size: 0.6875rem; + color: #6c757d; + white-space: nowrap; +} + +.actions-block { + display: flex; + align-items: center; + gap: 6px; + flex-shrink: 0; +} + +/* Botones con aspecto nativo Luna Windows XP */ +.xp-tool-btn { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 8px; + border: 1px solid #7f9db9; + border-radius: 3px; + background: linear-gradient(180deg, #ffffff 0%, #ebe7d6 60%, #ded9c4 100%); + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; + color: #000000; + text-decoration: none; + box-shadow: inset 0 1px 0 #ffffff; + cursor: pointer; + transition: all var(--transition-fast); +} + +.xp-tool-btn:hover { + border-color: #ffaa22; + background: linear-gradient(180deg, #fff7e6 0%, #ffd488 100%); +} + +.xp-tool-btn:active { + transform: translateY(1px); +} + +.xp-btn-primary { + border-color: #003c74; + background: linear-gradient(180deg, #3d95ff 0%, #0055ea 100%); + color: #ffffff; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6); +} + +.xp-btn-primary:hover { + background: linear-gradient(180deg, #5eb5f7 0%, #1e69ba 100%); + border-color: #002e5b; + color: #ffffff; +} + +.btn-icon { + width: 12px; + height: 12px; + flex-shrink: 0; +} + +/* Contenedor del PDF */ +.xp-pdf-viewport { + flex: 1; + min-height: 0; + width: 100%; + background: #323639; + position: relative; + overflow: hidden; +} + +.pdf-embed-object, +.pdf-embed-frame { + width: 100%; + height: 100%; + border: none; + display: block; +} + +.pdf-fallback-card { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + padding: 24px; + text-align: center; + color: #f0f3f8; + font-family: var(--font-sans); + gap: 12px; + background: #212529; +} + +.fallback-text { + font-size: 0.8125rem; + color: #ced4da; + margin: 0; +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-cv/retro-cv.component.html b/src/frontend/src/app/presentation/features/retro-os/components/retro-cv/retro-cv.component.html new file mode 100644 index 0000000..bc1bb2d --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-cv/retro-cv.component.html @@ -0,0 +1,64 @@ +
+ +
+
+ + CV Bryan Baño.pdf + (154 KB) +
+ + +
+ + +
+ + + +
+
diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-cv/retro-cv.component.ts b/src/frontend/src/app/presentation/features/retro-os/components/retro-cv/retro-cv.component.ts new file mode 100644 index 0000000..6a82665 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-cv/retro-cv.component.ts @@ -0,0 +1,20 @@ +import { Component, ChangeDetectionStrategy, inject } from '@angular/core'; +import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; +import { I18nService } from '../../../../../application/services/i18n.service'; + +@Component({ + selector: 'app-retro-cv', + standalone: true, + templateUrl: './retro-cv.component.html', + styleUrl: './retro-cv.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RetroCvComponent { + private readonly sanitizer = inject(DomSanitizer); + protected readonly i18n = inject(I18nService); + + readonly pdfUrl = '/assets/CV%20Bryan%20Ba%C3%B1o%20.pdf'; + readonly safePdfUrl: SafeResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl( + `${this.pdfUrl}#view=FitH` + ); +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-desktop-icons/retro-desktop-icons.component.css b/src/frontend/src/app/presentation/features/retro-os/components/retro-desktop-icons/retro-desktop-icons.component.css new file mode 100644 index 0000000..6473415 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-desktop-icons/retro-desktop-icons.component.css @@ -0,0 +1,68 @@ +:host { + display: block; +} + +.xp-icons-column { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px 12px; + width: 85px; +} + +.xp-desktop-icon { + display: flex; + flex-direction: column; + align-items: center; + gap: 3px; + background: transparent; + border: 1px solid transparent; + border-radius: 2px; + padding: 4px 2px; + cursor: pointer; + text-align: center; + color: #ffffff; + transition: all var(--transition-fast); + outline: none; +} + +.xp-desktop-icon:hover { + background: rgba(49, 106, 197, 0.4); + border: 1px dotted rgba(255, 255, 255, 0.8); +} + +.xp-desktop-icon:focus-visible, +.xp-desktop-icon:active { + background: rgba(11, 62, 146, 0.65); + border: 1px dotted #ffffff; +} + +.xp-icon-graphic { + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + filter: drop-shadow(0 3px 4px rgba(0, 0, 0, 0.4)); + transition: transform var(--transition-fast); +} + +.xp-desktop-icon:hover .xp-icon-graphic { + transform: scale(1.05); +} + +.xp-icon-svg { + width: 40px; + height: 40px; +} + +.xp-icon-title { + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; + color: #ffffff; + line-height: 1.15; + text-shadow: 1px 1px 1px #000000, 1px 2px 2px rgba(0, 0, 0, 0.95); + word-break: break-word; + user-select: none; +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-desktop-icons/retro-desktop-icons.component.html b/src/frontend/src/app/presentation/features/retro-os/components/retro-desktop-icons/retro-desktop-icons.component.html new file mode 100644 index 0000000..ca41928 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-desktop-icons/retro-desktop-icons.component.html @@ -0,0 +1,97 @@ +
+ @for (icon of icons(); track icon.id) { + + } +
diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-desktop-icons/retro-desktop-icons.component.ts b/src/frontend/src/app/presentation/features/retro-os/components/retro-desktop-icons/retro-desktop-icons.component.ts new file mode 100644 index 0000000..4dfd4b5 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-desktop-icons/retro-desktop-icons.component.ts @@ -0,0 +1,18 @@ +import { Component, ChangeDetectionStrategy, input, output } from '@angular/core'; +import { DesktopIcon, WindowId } from '../../../../../domain/models/retro-os.model'; + +@Component({ + selector: 'app-retro-desktop-icons', + standalone: true, + templateUrl: './retro-desktop-icons.component.html', + styleUrl: './retro-desktop-icons.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RetroDesktopIconsComponent { + readonly icons = input.required(); + readonly openWindow = output(); + + onIconClick(windowId: WindowId): void { + this.openWindow.emit(windowId); + } +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-game-canvas/retro-game-canvas.component.css b/src/frontend/src/app/presentation/features/retro-os/components/retro-game-canvas/retro-game-canvas.component.css new file mode 100644 index 0000000..02a8dbe --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-game-canvas/retro-game-canvas.component.css @@ -0,0 +1,139 @@ +:host { + display: block; + width: 100%; + height: 100%; + background: #ece9d8; + user-select: none; + font-family: var(--font-sans); +} + +.xp-game-wrapper { + display: flex; + flex-direction: column; + align-items: center; + padding: 6px; + gap: 6px; + height: 100%; +} + +/* Barra Superior de Estadísticas y Acciones */ +.xp-game-header { + width: 100%; + max-width: 300px; + display: flex; + align-items: center; + justify-content: space-between; + background: #f0ede0; + border: 1px solid #7f9db9; + border-radius: 3px; + padding: 4px 8px; + box-shadow: inset 0 1px 0 #ffffff; +} + +.xp-hud-stats { display: flex; gap: 10px; } +.hud-item { display: flex; align-items: center; gap: 4px; } +.hud-tag { font-size: 0.625rem; font-weight: 700; color: #333333; } +.hud-num { font-size: 0.8125rem; font-weight: 700; color: #0055ea; } +.hud-num.highlight { color: #d83a17; } +.xp-hud-actions { display: flex; gap: 4px; } + +/* Botones Clásicos Windows XP con borde naranja al hacer hover */ +.xp-classic-btn { + background: linear-gradient(180deg, #ffffff 0%, #ece9d8 100%); + border: 1px solid #7f9db9; + border-radius: 3px; + color: #000000; + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; + padding: 2px 8px; + cursor: pointer; + box-shadow: inset 0 1px 0 #ffffff, 0 1px 2px rgba(0, 0, 0, 0.15); + transition: all var(--transition-fast); +} + +.xp-classic-btn:hover:not(:disabled) { + border-color: #f29536; + background: linear-gradient(180deg, #fff7ea 0%, #ffdca8 100%); + box-shadow: inset 0 1px 0 #ffffff, 0 0 4px rgba(242, 149, 54, 0.6); +} + +.xp-classic-btn:active:not(:disabled) { background: #d8d4c2; transform: translateY(1px); } +.xp-classic-btn:disabled { opacity: 0.5; cursor: not-allowed; } + +/* Marco del Tablero Canvas */ +.xp-canvas-box { + position: relative; + width: 300px; + height: 300px; + border: 2px inset #7f9db9; + border-radius: 2px; + background: #1c2833; + cursor: pointer; + box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.4); +} + +.xp-snake-canvas { width: 300px; height: 300px; display: block; } + +/* Banner de Instrucción en la parte inferior del canvas */ +.xp-start-banner { + position: absolute; + bottom: 8px; + left: 8px; + right: 8px; + background: rgba(0, 0, 0, 0.78); + color: #ffffff; + font-size: 0.6875rem; + text-align: center; + padding: 4px 8px; + border-radius: 3px; + border: 1px solid rgba(255, 255, 255, 0.3); + pointer-events: none; +} + +/* Card de Game Over */ +.xp-gameover-card { + position: absolute; + inset: 20px; + background: rgba(236, 233, 216, 0.96); + border: 2px solid #0055ea; + border-radius: 6px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 12px; + text-align: center; +} + +.gameover-text { font-size: 1rem; font-weight: 700; color: #d83a17; } +.gameover-pts { font-size: 0.8125rem; font-weight: 600; color: #000000; } +.xp-save-row { display: flex; gap: 4px; } +.xp-input-tag { width: 50px; text-align: center; font-weight: 700; border: 1px solid #7f9db9; font-size: 0.8125rem; text-transform: uppercase; } +.xp-saved-label { font-size: 0.6875rem; color: #2e7d32; font-weight: 600; } +.btn-retry { padding: 4px 12px; } + +/* Controles Direccionales D-Pad */ +.xp-dpad-container { width: 100%; max-width: 300px; display: flex; justify-content: center; } +.xp-dpad-grid { display: grid; grid-template-columns: repeat(3, 36px); grid-template-rows: repeat(2, 30px); gap: 4px; } +.xp-arrow-btn { + background: linear-gradient(180deg, #ffffff 0%, #ece9d8 100%); + border: 1px solid #7f9db9; + border-radius: 3px; + color: #0055ea; + font-size: 0.8125rem; + font-weight: 900; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + box-shadow: inset 0 1px 0 #ffffff, 0 1px 2px rgba(0, 0, 0, 0.15); +} +.xp-arrow-btn:hover { border-color: #f29536; background: linear-gradient(180deg, #fff7ea 0%, #ffdca8 100%); } +.xp-arrow-btn:active { background: #d8d4c2; transform: translateY(1px); } +.xp-arrow-btn.up { grid-column: 2; grid-row: 1; } +.xp-arrow-btn.left { grid-column: 1; grid-row: 2; } +.xp-arrow-btn.down { grid-column: 2; grid-row: 2; } +.xp-arrow-btn.right { grid-column: 3; grid-row: 2; } diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-game-canvas/retro-game-canvas.component.html b/src/frontend/src/app/presentation/features/retro-os/components/retro-game-canvas/retro-game-canvas.component.html new file mode 100644 index 0000000..3f417e8 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-game-canvas/retro-game-canvas.component.html @@ -0,0 +1,89 @@ +
+ +
+
+
+ {{ i18n.t().retroGameScore }} + {{ score() }} +
+
+ {{ i18n.t().retroGameHigh }} + {{ highScore() }} +
+
+ +
+ + +
+
+ + +
+ + + + @if (!isPlaying() && !isGameOver()) { +
+ {{ i18n.t().retroGameBanner }} +
+ } + + + @if (isGameOver()) { +
+ {{ i18n.t().retroGameOver }} + {{ i18n.t().retroGameScoreLabel }} {{ score() }} + + @if (!isScoreSaved() && score() > 0) { +
+ + +
+ } + + @if (isScoreSaved()) { + {{ i18n.t().retroGameSaved }} + } + + +
+ } +
+ + +
+
+ + + + +
+
+
diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-game-canvas/retro-game-canvas.component.ts b/src/frontend/src/app/presentation/features/retro-os/components/retro-game-canvas/retro-game-canvas.component.ts new file mode 100644 index 0000000..df8e1ca --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-game-canvas/retro-game-canvas.component.ts @@ -0,0 +1,356 @@ +import { + Component, + AfterViewInit, + OnDestroy, + ElementRef, + viewChild, + ChangeDetectionStrategy, + signal, + inject, + HostListener +} from '@angular/core'; +import { RetroAudioService } from '../../../../../application/services/retro-audio.service'; +import { RetroScorePort } from '../../../../../domain/ports/retro-score.port'; +import { Direction, Point } from '../../../../../domain/models/retro-game.model'; +import { I18nService } from '../../../../../application/services/i18n.service'; + +const GRID_SIZE = 15; +const CELL_SIZE = 20; // 15 * 20 = 300px +const TICK_RATE_MS = 110; + +@Component({ + selector: 'app-retro-game-canvas', + standalone: true, + templateUrl: './retro-game-canvas.component.html', + styleUrl: './retro-game-canvas.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RetroGameCanvasComponent implements AfterViewInit, OnDestroy { + private readonly audio = inject(RetroAudioService); + private readonly scorePort = inject(RetroScorePort); + protected readonly i18n = inject(I18nService); + + protected readonly canvasRef = viewChild>('gameCanvas'); + private ctx: CanvasRenderingContext2D | null = null; + + protected readonly score = signal(0); + protected readonly highScore = signal(0); + protected readonly isGameOver = signal(false); + protected readonly isPaused = signal(false); + protected readonly isPlaying = signal(false); + protected readonly playerTag = signal('XP'); + protected readonly isScoreSaved = signal(false); + + private snake: Point[] = [{ x: 7, y: 7 }, { x: 7, y: 8 }, { x: 7, y: 9 }]; + private direction: Direction = 'UP'; + private nextDirection: Direction = 'UP'; + private food: Point = { x: 7, y: 3 }; + + private animationFrameId: number | null = null; + private lastTickTime = 0; + + ngAfterViewInit(): void { + const canvas = this.canvasRef()?.nativeElement; + if (!canvas) return; + this.ctx = canvas.getContext('2d'); + if (this.ctx) { + this.ctx.imageSmoothingEnabled = false; + } + this.loadHighScore(); + // Dibujar inmediatamente el tablero con la serpiente y la manzana 100% visibles + this.resetState(); + this.render(); + } + + resetState(): void { + this.snake = [{ x: 7, y: 7 }, { x: 7, y: 8 }, { x: 7, y: 9 }]; + this.direction = 'UP'; + this.nextDirection = 'UP'; + this.food = { x: 7, y: 3 }; + this.score.set(0); + this.isGameOver.set(false); + this.isPaused.set(false); + this.isPlaying.set(false); + this.isScoreSaved.set(false); + } + + startGame(): void { + if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + this.resetState(); + this.spawnFood(); + this.isPlaying.set(true); + + this.audio.playClick(); + this.lastTickTime = performance.now(); + this.loop(performance.now()); + } + + onCanvasClick(): void { + if (!this.isPlaying()) { + this.startGame(); + } else if (this.isGameOver()) { + this.startGame(); + } + } + + togglePause(): void { + if (!this.isPlaying() || this.isGameOver()) return; + this.isPaused.update(p => !p); + this.audio.playClick(); + } + + @HostListener('window:keydown', ['$event']) + handleKeyDown(event: KeyboardEvent): void { + const validKeys = [ + 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', + 'w', 's', 'a', 'd', 'W', 'S', 'A', 'D', ' ', 'Enter' + ]; + + if (!validKeys.includes(event.key)) return; + + // Prevenir el scroll del navegador con las flechas + event.preventDefault(); + + // Si aún no está jugando, iniciar inmediatamente al pulsar cualquier flecha o tecla de control + if (!this.isPlaying() || this.isGameOver()) { + this.startGame(); + if (event.key === 'ArrowDown' || event.key === 's' || event.key === 'S') this.nextDirection = 'DOWN'; + if (event.key === 'ArrowLeft' || event.key === 'a' || event.key === 'A') this.nextDirection = 'LEFT'; + if (event.key === 'ArrowRight' || event.key === 'd' || event.key === 'D') this.nextDirection = 'RIGHT'; + return; + } + + switch (event.key) { + case 'ArrowUp': + case 'w': + case 'W': + if (this.direction !== 'DOWN') this.nextDirection = 'UP'; + break; + case 'ArrowDown': + case 's': + case 'S': + if (this.direction !== 'UP') this.nextDirection = 'DOWN'; + break; + case 'ArrowLeft': + case 'a': + case 'A': + if (this.direction !== 'RIGHT') this.nextDirection = 'LEFT'; + break; + case 'ArrowRight': + case 'd': + case 'D': + if (this.direction !== 'LEFT') this.nextDirection = 'RIGHT'; + break; + case ' ': + this.togglePause(); + break; + } + } + + setDirection(newDir: Direction): void { + if (!this.isPlaying() || this.isGameOver()) { + this.startGame(); + this.nextDirection = newDir; + return; + } + if (this.isPaused()) return; + if (newDir === 'UP' && this.direction !== 'DOWN') this.nextDirection = 'UP'; + if (newDir === 'DOWN' && this.direction !== 'UP') this.nextDirection = 'DOWN'; + if (newDir === 'LEFT' && this.direction !== 'RIGHT') this.nextDirection = 'LEFT'; + if (newDir === 'RIGHT' && this.direction !== 'LEFT') this.nextDirection = 'RIGHT'; + } + + private loop = (timestamp: number): void => { + if (!this.isPlaying()) return; + + if (!this.isPaused() && !this.isGameOver()) { + if (timestamp - this.lastTickTime >= TICK_RATE_MS) { + this.update(); + this.lastTickTime = timestamp; + } + } + this.render(); + if (!this.isGameOver()) { + this.animationFrameId = requestAnimationFrame(this.loop); + } + }; + + private update(): void { + this.direction = this.nextDirection; + const head = { ...this.snake[0] }; + + switch (this.direction) { + case 'UP': head.y -= 1; break; + case 'DOWN': head.y += 1; break; + case 'LEFT': head.x -= 1; break; + case 'RIGHT': head.x += 1; break; + } + + // Choque con paredes + if (head.x < 0 || head.x >= GRID_SIZE || head.y < 0 || head.y >= GRID_SIZE) { + this.triggerGameOver(); + return; + } + + // Choque con el propio cuerpo + if (this.snake.some(segment => segment.x === head.x && segment.y === head.y)) { + this.triggerGameOver(); + return; + } + + this.snake.unshift(head); + + // Comer fruta + if (head.x === this.food.x && head.y === this.food.y) { + const nextScore = this.score() + 10; + this.score.set(nextScore); + if (nextScore > this.highScore()) { + this.highScore.set(nextScore); + } + this.audio.playSnakeEat(); + this.spawnFood(); + } else { + this.snake.pop(); + } + } + + private triggerGameOver(): void { + this.isGameOver.set(true); + this.audio.playGameOver(); + this.render(); // Renderizar el estado de choque final + } + + private spawnFood(): void { + let valid = false; + let newX = 0; + let newY = 0; + while (!valid) { + newX = Math.floor(Math.random() * GRID_SIZE); + newY = Math.floor(Math.random() * GRID_SIZE); + valid = !this.snake.some(s => s.x === newX && s.y === newY); + } + this.food = { x: newX, y: newY }; + } + + private render(): void { + if (!this.ctx) return; + const ctx = this.ctx; + const width = GRID_SIZE * CELL_SIZE; // 300 + const height = GRID_SIZE * CELL_SIZE; // 300 + + // Tablero estilo Arcade clásico con cuadrícula nítida + ctx.fillStyle = '#1c2833'; + ctx.fillRect(0, 0, width, height); + + // Cuadrícula suave de casillas + for (let r = 0; r < GRID_SIZE; r++) { + for (let c = 0; c < GRID_SIZE; c++) { + if ((r + c) % 2 === 0) { + ctx.fillStyle = '#212f3d'; + ctx.fillRect(c * CELL_SIZE, r * CELL_SIZE, CELL_SIZE, CELL_SIZE); + } + } + } + + // Borde exterior + ctx.strokeStyle = '#34495e'; + ctx.lineWidth = 2; + ctx.strokeRect(1, 1, width - 2, height - 2); + + // 1. DIBUJAR FRUTA (Manzana roja con tallo y hoja) + const fx = this.food.x * CELL_SIZE + CELL_SIZE / 2; + const fy = this.food.y * CELL_SIZE + CELL_SIZE / 2; + + ctx.save(); + // Brillo de la manzana + ctx.fillStyle = '#e74c3c'; + ctx.beginPath(); + ctx.arc(fx, fy, 8, 0, Math.PI * 2); + ctx.fill(); + + // Tallo marrón y hoja verde + ctx.fillStyle = '#795548'; + ctx.fillRect(fx - 1, fy - 10, 2, 4); + ctx.fillStyle = '#2ecc71'; + ctx.beginPath(); + ctx.ellipse(fx + 3, fy - 8, 3, 1.5, Math.PI / 4, 0, Math.PI * 2); + ctx.fill(); + ctx.restore(); + + // 2. DIBUJAR SERPIENTE (Verde brillante y con ojos claramente visibles) + this.snake.forEach((seg, i) => { + const isHead = i === 0; + const px = seg.x * CELL_SIZE; + const py = seg.y * CELL_SIZE; + + ctx.save(); + if (isHead) { + // Cabeza verde lima destacada + ctx.fillStyle = this.isGameOver() ? '#e74c3c' : '#2ecc71'; + ctx.fillRect(px + 1, py + 1, CELL_SIZE - 2, CELL_SIZE - 2); + + // Ojos blancos con pupilas negras + ctx.fillStyle = '#ffffff'; + if (this.direction === 'UP' || this.direction === 'DOWN') { + ctx.fillRect(px + 3, py + 4, 4, 4); + ctx.fillRect(px + 13, py + 4, 4, 4); + ctx.fillStyle = '#000000'; + ctx.fillRect(px + 4, py + (this.direction === 'UP' ? 4 : 6), 2, 2); + ctx.fillRect(px + 14, py + (this.direction === 'UP' ? 4 : 6), 2, 2); + } else { + ctx.fillRect(px + 4, py + 3, 4, 4); + ctx.fillRect(px + 4, py + 13, 4, 4); + ctx.fillStyle = '#000000'; + ctx.fillRect(px + (this.direction === 'LEFT' ? 4 : 6), py + 4, 2, 2); + ctx.fillRect(px + (this.direction === 'LEFT' ? 4 : 6), py + 14, 2, 2); + } + } else { + // Cuerpo verde esmeralda con borde + ctx.fillStyle = '#27ae60'; + ctx.fillRect(px + 2, py + 2, CELL_SIZE - 4, CELL_SIZE - 4); + ctx.strokeStyle = '#1e8449'; + ctx.lineWidth = 1; + ctx.strokeRect(px + 2, py + 2, CELL_SIZE - 4, CELL_SIZE - 4); + } + ctx.restore(); + }); + } + + submitScore(): void { + const finalScore = this.score(); + const tag = (this.playerTag() || 'XP').trim().toUpperCase().slice(0, 4); + if (finalScore <= 0 || this.isScoreSaved()) return; + + this.scorePort.saveScore({ + playerTag: tag, + gameCode: 'ZEN_SNAKE', + score: finalScore + }).subscribe({ + next: () => { + this.isScoreSaved.set(true); + this.audio.playHighScore(); + this.loadHighScore(); + } + }); + } + + private loadHighScore(): void { + this.scorePort.getTopScores('ZEN_SNAKE', 1).subscribe({ + next: (scores) => { + if (scores.length > 0 && scores[0].score > this.highScore()) { + this.highScore.set(scores[0].score); + } + } + }); + } + + ngOnDestroy(): void { + this.isPlaying.set(false); + if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); + } + } +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-scores/retro-scores.component.css b/src/frontend/src/app/presentation/features/retro-os/components/retro-scores/retro-scores.component.css new file mode 100644 index 0000000..20c8d7d --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-scores/retro-scores.component.css @@ -0,0 +1,116 @@ +:host { + display: block; + padding: var(--space-3); + background-color: var(--zen-bg-base); + font-family: var(--font-sans); + min-height: 100%; +} + +.scores-panel { + display: flex; + flex-direction: column; + gap: var(--space-3); +} + +.scores-header { + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid var(--zen-border-subtle); + padding-bottom: var(--space-2); +} + +.hall-badge { + font-size: 0.6875rem; + font-weight: 700; + color: var(--zen-accent-kintsugi); + letter-spacing: 0.08em; +} + +.game-name { + font-size: 1.1rem; + font-weight: 800; + color: var(--zen-text-primary); +} + +.refresh-btn { + background: var(--zen-bg-surface); + border: 1px solid var(--zen-border-subtle); + color: var(--zen-text-secondary); + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; + padding: 4px 8px; + border-radius: var(--zen-radius-xs); + cursor: pointer; +} + +.refresh-btn:hover { + border-color: var(--zen-accent-kintsugi); + color: var(--zen-text-primary); +} + +.table-wrapper { + overflow-x: auto; +} + +.scores-table { + width: 100%; + border-collapse: collapse; + font-size: 0.75rem; +} + +.scores-table th { + text-align: left; + padding: 6px 8px; + color: var(--zen-text-muted); + border-bottom: 1px solid var(--zen-border-subtle); + font-size: 0.625rem; + letter-spacing: 0.06em; +} + +.scores-table td { + padding: 6px 8px; + border-bottom: 1px solid rgba(255, 255, 255, 0.03); +} + +.rank-col { + width: 24px; + color: var(--zen-text-muted); + font-weight: 700; +} + +.tag-col { + color: var(--zen-text-primary); + font-weight: 700; + letter-spacing: 0.08em; +} + +.score-col { + color: var(--zen-accent-matcha); + font-weight: 700; +} + +.date-col { + color: var(--zen-text-muted); + font-size: 0.6875rem; +} + +.gold-rank .tag-col, +.gold-rank .score-col { + color: var(--zen-accent-kintsugi); +} + +.silver-rank .tag-col { + color: #d8dee9; +} + +.bronze-rank .tag-col { + color: var(--zen-accent-amber); +} + +.empty-state { + text-align: center; + padding: var(--space-4); + color: var(--zen-text-muted); +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-scores/retro-scores.component.html b/src/frontend/src/app/presentation/features/retro-os/components/retro-scores/retro-scores.component.html new file mode 100644 index 0000000..cb5d649 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-scores/retro-scores.component.html @@ -0,0 +1,38 @@ +
+
+
+ {{ i18n.t().retroScoresHeader }} +

{{ i18n.t().retroScoresTitle }}

+
+ +
+ +
+ + + + + + + + + + + @for (item of scores(); track item.id; let i = $index) { + + + + + + + } @empty { + + + + } + +
{{ i18n.t().retroScoresRank }}{{ i18n.t().retroScoresTag }}{{ i18n.t().retroScoresScore }}{{ i18n.t().retroScoresDate }}
{{ i + 1 }}{{ item.playerTag }}{{ item.score }}{{ item.createdAtUtc.slice(0, 10) }}
{{ i18n.t().retroScoresEmpty }}
+
+
diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-scores/retro-scores.component.ts b/src/frontend/src/app/presentation/features/retro-os/components/retro-scores/retro-scores.component.ts new file mode 100644 index 0000000..735dc6a --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-scores/retro-scores.component.ts @@ -0,0 +1,41 @@ +import { Component, OnInit, ChangeDetectionStrategy, signal, inject } from '@angular/core'; +import { RetroScorePort } from '../../../../../domain/ports/retro-score.port'; +import { RetroScore } from '../../../../../domain/models/retro-game.model'; +import { RetroAudioService } from '../../../../../application/services/retro-audio.service'; +import { I18nService } from '../../../../../application/services/i18n.service'; + +@Component({ + selector: 'app-retro-scores', + standalone: true, + templateUrl: './retro-scores.component.html', + styleUrl: './retro-scores.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RetroScoresComponent implements OnInit { + private readonly scorePort = inject(RetroScorePort); + private readonly audio = inject(RetroAudioService); + protected readonly i18n = inject(I18nService); + + protected readonly scores = signal([]); + protected readonly isLoading = signal(false); + + ngOnInit(): void { + this.loadScores(); + } + + loadScores(): void { + this.isLoading.set(true); + this.scorePort.getTopScores('ZEN_SNAKE', 10).subscribe({ + next: (data) => { + this.scores.set(data); + this.isLoading.set(false); + }, + error: () => this.isLoading.set(false) + }); + } + + onRefresh(): void { + this.audio.playClick(); + this.loadScores(); + } +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-settings/retro-settings.component.css b/src/frontend/src/app/presentation/features/retro-os/components/retro-settings/retro-settings.component.css new file mode 100644 index 0000000..919c143 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-settings/retro-settings.component.css @@ -0,0 +1,124 @@ +:host { + display: block; + padding: var(--space-4); + background-color: var(--zen-bg-base); + color: var(--zen-text-primary); + font-family: var(--font-sans); + min-height: 100%; +} + +.settings-panel { + display: flex; + flex-direction: column; + gap: var(--space-4); +} + +.settings-group { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +.group-label { + font-size: 0.75rem; + font-weight: 800; + color: var(--zen-accent-kintsugi); + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.theme-options { + display: flex; + flex-direction: column; + gap: 6px; +} + +.theme-btn { + display: flex; + align-items: center; + gap: var(--space-2); + background: var(--zen-bg-surface); + border: 1px solid var(--zen-border-subtle); + border-radius: var(--zen-radius-xs); + padding: 6px var(--space-2); + color: var(--zen-text-secondary); + font-family: var(--font-sans); + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + text-align: left; + transition: all var(--transition-fast); +} + +.theme-btn:hover { + border-color: var(--zen-border-focus); + color: var(--zen-text-primary); +} + +.theme-btn.active { + border-color: var(--zen-accent-kintsugi); + background: var(--zen-bg-elevated); + color: var(--zen-text-primary); +} + +.swatch { + width: 12px; + height: 12px; + border-radius: 50%; + flex-shrink: 0; + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.swatch-zen { background-color: #c59b27; } +.swatch-amber { background-color: #ffb833; } +.swatch-matrix { background-color: #33ff77; } + +.settings-divider { + height: 1px; + background: var(--zen-border-subtle); +} + +.toggle-row { + display: flex; + justify-content: space-between; + align-items: center; + background: var(--zen-bg-surface); + border: 1px solid var(--zen-border-subtle); + border-radius: var(--zen-radius-xs); + padding: 8px var(--space-2); +} + +.toggle-info { + display: flex; + flex-direction: column; +} + +.toggle-name { + font-size: 0.8125rem; + font-weight: 700; + color: var(--zen-text-primary); +} + +.toggle-desc { + font-size: 0.75rem; + color: var(--zen-text-muted); +} + +.zen-toggle-switch { + background: #151a24; + border: 1px solid var(--zen-border-subtle); + color: var(--zen-text-muted); + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 700; + padding: 3px 10px; + border-radius: var(--zen-radius-xs); + cursor: pointer; + transition: all var(--transition-fast); +} + +.zen-toggle-switch.on { + background: rgba(78, 155, 111, 0.2); + border-color: var(--zen-accent-matcha); + color: var(--zen-accent-matcha); +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-settings/retro-settings.component.html b/src/frontend/src/app/presentation/features/retro-os/components/retro-settings/retro-settings.component.html new file mode 100644 index 0000000..c927e88 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-settings/retro-settings.component.html @@ -0,0 +1,76 @@ +
+
+ {{ i18n.t().retroSetPalette }} +
+ + + + + +
+
+ +
+ +
+ {{ i18n.t().retroSetVisualAudio }} + +
+
+ {{ i18n.t().retroSetScanlines }} + {{ i18n.t().retroSetScanlinesDesc }} +
+ +
+ +
+
+ {{ i18n.t().retroSetSound }} + {{ i18n.t().retroSetSoundDesc }} +
+ +
+
+
diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-settings/retro-settings.component.ts b/src/frontend/src/app/presentation/features/retro-os/components/retro-settings/retro-settings.component.ts new file mode 100644 index 0000000..dec88da --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-settings/retro-settings.component.ts @@ -0,0 +1,28 @@ +import { Component, ChangeDetectionStrategy, inject } from '@angular/core'; +import { RetroOsStore } from '../../../../../application/stores/retro-os.store'; +import { I18nService } from '../../../../../application/services/i18n.service'; +import { ZenOsTheme } from '../../../../../domain/models/retro-os.model'; + +@Component({ + selector: 'app-retro-settings', + standalone: true, + templateUrl: './retro-settings.component.html', + styleUrl: './retro-settings.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RetroSettingsComponent { + protected readonly store = inject(RetroOsStore); + protected readonly i18n = inject(I18nService); + + onSelectTheme(theme: ZenOsTheme): void { + this.store.setTheme(theme); + } + + onToggleScanlines(): void { + this.store.toggleScanlines(); + } + + onToggleSound(): void { + this.store.toggleSound(); + } +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-start-menu/retro-start-menu.component.css b/src/frontend/src/app/presentation/features/retro-os/components/retro-start-menu/retro-start-menu.component.css new file mode 100644 index 0000000..63af24e --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-start-menu/retro-start-menu.component.css @@ -0,0 +1,193 @@ +:host { + display: block; + position: absolute; + bottom: 30px; + left: 0; + z-index: 500; +} + +.xp-start-menu { + width: 360px; + background: #ffffff; + border: 1px solid #002e88; + border-radius: 6px 6px 0 0; + box-shadow: 2px 2px 14px rgba(0, 0, 0, 0.7); + display: flex; + flex-direction: column; + overflow: hidden; + font-family: var(--font-sans); + user-select: none; +} + +.start-header { + height: 48px; + background: linear-gradient(180deg, #0055ea 0%, #0040be 100%); + border-bottom: 1px solid #ff9900; + display: flex; + align-items: center; + gap: 10px; + padding: 0 12px; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5); +} + +.user-avatar-frame { + width: 34px; + height: 34px; + border: 2px solid #ffffff; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); + background: #ffb300; + overflow: hidden; +} + +.user-avatar-placeholder { + width: 100%; + height: 100%; + background: linear-gradient(135deg, #f57f17 0%, #ffd54f 100%); +} + +.user-name { + font-size: 0.9375rem; + font-weight: 700; + color: #ffffff; + text-shadow: 1px 1px 2px #001e66; +} + +.start-body { + display: flex; + height: 310px; +} + +.start-col-left { + width: 52%; + background: #ffffff; + padding: 6px 4px; + display: flex; + flex-direction: column; + gap: 2px; +} + +.start-col-right { + width: 48%; + background: #d3e5fa; + border-left: 1px solid #95bcee; + padding: 6px 4px; + display: flex; + flex-direction: column; + gap: 2px; +} + +.start-item { + display: flex; + align-items: center; + gap: 6px; + padding: 3px 6px; + border-radius: 2px; + cursor: pointer; + color: #333333; +} + +.start-item:hover { + background: #316ac5; + color: #ffffff; +} + +.start-item.pinned .item-title { + font-size: 0.6875rem; + font-weight: 700; +} + +.item-texts { + display: flex; + flex-direction: column; +} + +.item-sub { + font-size: 0.5625rem; + color: #777777; +} + +.start-item:hover .item-sub { + color: #e0edfe; +} + +.item-title { + font-size: 0.6875rem; +} + +.item-title.bold { + font-weight: 700; +} + +.ie-icon { + font-family: 'Times New Roman', serif; + font-weight: 900; + color: #0078d7; + font-size: 1rem; +} + +.start-sep { + height: 1px; + background: #e0e0e0; + margin: 3px 6px; +} + +.start-sep.blue { + background: #b5d2f6; +} + +.start-all-progs { + margin-top: auto; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + padding: 6px; + border-top: 1px solid #d5d5d5; + font-size: 0.6875rem; + font-weight: 700; + cursor: pointer; +} + +.start-all-progs:hover { + background: #316ac5; + color: #ffffff; +} + +.green-arrow { + color: #2e7d32; + font-size: 0.625rem; +} + +.start-footer { + height: 40px; + background: linear-gradient(180deg, #1f5cc7 0%, #1545b7 100%); + border-top: 1px solid #0b2e80; + display: flex; + align-items: center; + justify-content: flex-end; + gap: 12px; + padding: 0 14px; +} + +.footer-action-btn { + background: transparent; + border: none; + color: #ffffff; + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; + display: flex; + align-items: center; + gap: 4px; + cursor: pointer; +} + +.footer-action-btn:hover { + text-decoration: underline; +} + +.power-icon { + color: #ff5252; + font-size: 0.8125rem; +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-start-menu/retro-start-menu.component.html b/src/frontend/src/app/presentation/features/retro-os/components/retro-start-menu/retro-start-menu.component.html new file mode 100644 index 0000000..57cff6b --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-start-menu/retro-start-menu.component.html @@ -0,0 +1,104 @@ +
+ +
+
+
+
+ Bryan +
+ + +
+ +
+
+ e +
+ {{ i18n.t().retroMenuInternet }} + Internet Explorer +
+
+
+ ✉ +
+ {{ i18n.t().retroMenuEmail }} + Outlook Express +
+
+ +
+ +
+ 🎮 + {{ i18n.t().retroMenuGames }} +
+
+ ▶ + {{ i18n.t().retroMenuWmp }} +
+
+ 📝 + {{ i18n.t().retroMenuNotepad }} +
+ +
+ {{ i18n.t().retroMenuAllProgs }} + ▶ +
+
+ + +
+
+ 📁 + {{ i18n.t().retroMenuDocs }} +
+
+ 🖼 + {{ i18n.t().retroMenuPics }} +
+
+ 🎵 + {{ i18n.t().retroMenuMusic }} +
+
+ 💻 + {{ i18n.t().retroMenuPc }} +
+ +
+ +
+ ⚙ + {{ i18n.t().retroMenuSettings }} +
+
+ 🏆 + {{ i18n.t().retroMenuScores }} +
+ +
+ +
+ ❓ + {{ i18n.t().retroMenuHelp }} +
+
+ 🔍 + {{ i18n.t().retroMenuSearch }} +
+
+
+ + +
+ + +
+
diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-start-menu/retro-start-menu.component.ts b/src/frontend/src/app/presentation/features/retro-os/components/retro-start-menu/retro-start-menu.component.ts new file mode 100644 index 0000000..27cd8ca --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-start-menu/retro-start-menu.component.ts @@ -0,0 +1,24 @@ +import { Component, ChangeDetectionStrategy, output, inject } from '@angular/core'; +import { WindowId } from '../../../../../domain/models/retro-os.model'; +import { I18nService } from '../../../../../application/services/i18n.service'; + +@Component({ + selector: 'app-retro-start-menu', + standalone: true, + templateUrl: './retro-start-menu.component.html', + styleUrl: './retro-start-menu.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RetroStartMenuComponent { + protected readonly i18n = inject(I18nService); + + readonly close = output(); + readonly openWindow = output(); + + onSelect(windowId?: WindowId): void { + if (windowId) { + this.openWindow.emit(windowId); + } + this.close.emit(); + } +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-window/retro-window.component.css b/src/frontend/src/app/presentation/features/retro-os/components/retro-window/retro-window.component.css new file mode 100644 index 0000000..9054cd0 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-window/retro-window.component.css @@ -0,0 +1,157 @@ +:host { + display: block; +} + +.retro-window.xp-window { + position: absolute; + background-color: #0055ea; + border: 3px solid #0055ea; + border-radius: 8px 8px 0 0; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.65), 0 2px 8px rgba(0, 0, 0, 0.4); + display: flex; + flex-direction: column; + overflow: hidden; + user-select: none; + max-width: calc(100% - 16px); + pointer-events: auto !important; + transition: box-shadow var(--transition-fast); +} + +.xp-window.window-active { + box-shadow: 0 14px 40px rgba(0, 0, 0, 0.8), 0 0 12px rgba(0, 85, 234, 0.4); +} + +.retro-window.xp-window.window-maximized { + top: 0 !important; + left: 0 !important; + width: 100% !important; + height: calc(100% - 28px) !important; + max-width: 100% !important; + border-radius: 0 !important; + border: none !important; + box-shadow: none !important; +} + +.xp-window.window-dragging { + opacity: 0.94; + cursor: grabbing; +} + +/* Barra de Título Luna Blue Windows XP */ +.xp-titlebar { + height: 28px; + background: linear-gradient( + 180deg, + #0058ee 0%, + #3593ff 8%, + #0057e5 20%, + #0055eb 60%, + #0047c7 100% + ); + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 4px 0 6px; + cursor: grab; + flex-shrink: 0; +} + +.xp-header-left { + display: flex; + align-items: center; + gap: 6px; + min-width: 0; + pointer-events: none; +} + +.xp-win-icon { + width: 15px; + height: 15px; + border-radius: 2px; + background: linear-gradient(135deg, #f5ba13 0%, #e2a632 100%); + border: 1px solid #ffffff; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); + flex-shrink: 0; +} + +.xp-window-title { + font-family: var(--font-sans); + font-size: 0.8125rem; + font-weight: 700; + color: #ffffff; + text-shadow: 1px 1px 1px #002277, 0 1px 2px rgba(0, 0, 0, 0.6); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + letter-spacing: 0.01em; +} + +/* Botones Clásicos Luna */ +.xp-caption-buttons { + display: flex; + align-items: center; + gap: 3px; + flex-shrink: 0; +} + +.xp-btn-caption { + width: 21px; + height: 21px; + border: 1px solid #ffffff; + border-radius: 3px; + background: linear-gradient(180deg, #3d95ff 0%, #0055ea 100%); + color: #ffffff; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + padding: 0; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 1px 2px rgba(0, 0, 0, 0.4); + transition: all var(--transition-fast); +} + +.xp-btn-caption:hover { + background: linear-gradient(180deg, #5eb5f7 0%, #1e69ba 100%); +} + +.xp-btn-caption:active { + transform: translateY(1px); +} + +.btn-symbol { + font-family: var(--font-sans); + font-size: 0.6875rem; + font-weight: 900; + line-height: 1; +} + +.btn-min .btn-symbol { + margin-top: -3px; +} + +.btn-close { + background: linear-gradient(180deg, #e05338 0%, #c42910 100%); +} + +.btn-close:hover { + background: linear-gradient(180deg, #f5745d 0%, #e03b1f 100%); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8), 0 0 6px rgba(230, 60, 30, 0.8); +} + +/* Área de Contenido Windows XP (Color Clásico #ece9d8) */ +.xp-client-area { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + background-color: #ece9d8; + border: 1px solid #7f9db9; + border-top: none; + pointer-events: auto !important; + color: #000000; +} + +.xp-window.window-maximized .xp-client-area { + border-left: none; + border-right: none; + border-bottom: none; +} diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-window/retro-window.component.html b/src/frontend/src/app/presentation/features/retro-os/components/retro-window/retro-window.component.html new file mode 100644 index 0000000..229be25 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-window/retro-window.component.html @@ -0,0 +1,67 @@ +
+ +
+
+ + {{ window().title }} +
+ + +
+ + + +
+
+ + +
+ +
+
diff --git a/src/frontend/src/app/presentation/features/retro-os/components/retro-window/retro-window.component.ts b/src/frontend/src/app/presentation/features/retro-os/components/retro-window/retro-window.component.ts new file mode 100644 index 0000000..bf53c1a --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/components/retro-window/retro-window.component.ts @@ -0,0 +1,109 @@ +import { + Component, + ChangeDetectionStrategy, + input, + output, + signal, + ElementRef, + viewChild, + inject +} from '@angular/core'; +import { RetroWindow, WindowId, WindowPosition } from '../../../../../domain/models/retro-os.model'; +import { I18nService } from '../../../../../application/services/i18n.service'; + +@Component({ + selector: 'app-retro-window', + standalone: true, + templateUrl: './retro-window.component.html', + styleUrl: './retro-window.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RetroWindowComponent { + protected readonly i18n = inject(I18nService); + + readonly window = input.required(); + readonly isActive = input(false); + + readonly close = output(); + readonly minimize = output(); + readonly toggleMaximize = output(); + readonly focus = output(); + readonly move = output<{ id: WindowId; position: WindowPosition }>(); + + protected readonly headerRef = viewChild>('windowHeader'); + protected readonly isDragging = signal(false); + + private dragStartX = 0; + private dragStartY = 0; + private initialPosX = 0; + private initialPosY = 0; + + onPointerDown(event: PointerEvent): void { + if (event.button !== 0) return; // Solo botón principal + this.focus.emit(this.window().id); + + // No permitir arrastrar si la ventana está maximizada + if (this.window().isMaximized) return; + + // Si se hizo clic en un botón de control (cerrar, minimizar, etc.), no iniciar arrastre + const target = event.target as HTMLElement; + if (target.closest('button') || target.closest('.xp-caption-buttons')) { + return; + } + + const header = this.headerRef()?.nativeElement; + if (!header || !target.closest('.window-header-drag-area')) return; + + this.isDragging.set(true); + this.dragStartX = event.clientX; + this.dragStartY = event.clientY; + this.initialPosX = this.window().position.x; + this.initialPosY = this.window().position.y; + + header.setPointerCapture(event.pointerId); + } + + onPointerMove(event: PointerEvent): void { + if (!this.isDragging()) return; + + const dx = event.clientX - this.dragStartX; + const dy = event.clientY - this.dragStartY; + + const newX = Math.max(4, this.initialPosX + dx); + const newY = Math.max(4, this.initialPosY + dy); + + this.move.emit({ + id: this.window().id, + position: { x: newX, y: newY } + }); + } + + onPointerUp(event: PointerEvent): void { + if (!this.isDragging()) return; + this.isDragging.set(false); + + const header = this.headerRef()?.nativeElement; + if (header && header.hasPointerCapture(event.pointerId)) { + header.releasePointerCapture(event.pointerId); + } + } + + onWindowClick(): void { + this.focus.emit(this.window().id); + } + + onMinimize(event: MouseEvent): void { + event.stopPropagation(); + this.minimize.emit(this.window().id); + } + + onToggleMaximize(event: MouseEvent): void { + event.stopPropagation(); + this.toggleMaximize.emit(this.window().id); + } + + onClose(event: MouseEvent): void { + event.stopPropagation(); + this.close.emit(this.window().id); + } +} diff --git a/src/frontend/src/app/presentation/features/retro-os/retro-os.component.css b/src/frontend/src/app/presentation/features/retro-os/retro-os.component.css new file mode 100644 index 0000000..7cfb441 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/retro-os.component.css @@ -0,0 +1,176 @@ +:host { + display: block; + width: 100%; + background-color: #121517; +} + +/* ============================================================================== + MONITOR CRT VINTAGE RECORTADO AL 100% (SIN BORDES VACÍOS) + ============================================================================== */ + +.vintage-monitor-wrapper { + position: relative; + width: 100%; + aspect-ratio: 1398 / 1564; + margin: 0 auto; + user-select: none; + background-color: #121517; +} + +/* Imagen del Monitor de Fondo (Ocupa el 100% del ancho sin márgenes vacíos) */ +.monitor-background-img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: contain; + z-index: 1; + pointer-events: none; +} + +/* Área de Pantalla CRT Calibrada al Milímetro (81.83% del ancho del chasis) */ +.monitor-screen-glass { + position: absolute; + top: 9.27%; + left: 8.80%; + width: 81.83%; + height: 56.91%; + overflow: hidden; + border-radius: 8px; + background: #000000; + box-shadow: inset 0 0 14px rgba(0, 0, 0, 0.9); + z-index: 10; +} + +/* Escritorio Windows XP dentro de la Pantalla */ +.xp-desktop-canvas { + position: relative; + width: 100%; + height: 100%; + background: #245edb url('/assets/images/xp.jpg') center center / cover no-repeat; + overflow: hidden; +} + +.crt-screen-glare { + position: absolute; + top: 0; + right: 0; + width: 45%; + height: 40%; + background: radial-gradient(ellipse at top right, rgba(255, 255, 255, 0.1) 0%, transparent 70%); + pointer-events: none; + z-index: 25; +} + +.xp-windows-layer { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 15; +} + +.xp-windows-layer > * { + pointer-events: auto; +} + +/* ============================================================================== + BARRA DE TAREAS LUNA BLUE DE ALTA FIDELIDAD (28px) + ============================================================================== */ + +.xp-custom-taskbar { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 28px; + background: linear-gradient(180deg, #3168d8 0%, #3f8cf3 10%, #245edb 22%, #245edb 90%, #1941a5 100%); + border-top: 1px solid #5896ff; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4); + display: flex; + align-items: center; + z-index: 28; +} + +/* Botón Inicio Clásico Verde */ +.xp-start-button { + height: 28px; + width: 88px; + background: linear-gradient(180deg, #3ca330 0%, #46b537 10%, #3c9b2f 25%, #2b7823 55%, #22661c 85%, #1b5416 100%); + border: 1px solid #1b5416; + border-left: none; + border-radius: 0 12px 12px 0; + display: flex; + align-items: center; + gap: 5px; + padding: 0 8px 0 5px; + cursor: pointer; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), 1px 0 3px rgba(0, 0, 0, 0.4); + transition: filter var(--transition-fast); +} + +.xp-start-button:hover, +.xp-start-button.active { filter: brightness(1.12); } +.xp-start-button:active { filter: brightness(0.95); } +.start-flag { width: 14px; height: 14px; display: flex; align-items: center; } +.xp-flag-svg { width: 14px; height: 14px; filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.5)); } + +.start-text { + font-family: var(--font-sans); + font-style: italic; + font-size: 0.8125rem; + font-weight: 700; + color: #ffffff; + text-shadow: 1px 1px 1px #0e3b0a; +} + +/* Quick Launch */ +.xp-quick-launch { display: flex; align-items: center; gap: 2px; padding: 0 4px; } +.ql-sep { width: 2px; height: 16px; border-left: 1px solid #1646b9; border-right: 1px solid #5896ff; margin-right: 3px; } +.ql-btn { background: transparent; border: none; font-size: 0.6875rem; cursor: pointer; padding: 1px 2px; } +.ql-btn:hover { background: rgba(255, 255, 255, 0.2); } + +/* Pestañas de Ventanas */ +.xp-task-tabs { display: flex; align-items: center; gap: 3px; flex: 1; overflow-x: auto; padding: 0 4px; } +.xp-tab-button { + height: 22px; + max-width: 120px; + background: linear-gradient(180deg, #3d80e8 0%, #2055be 100%); + border: 1px solid #163d8b; + border-radius: 2px; + color: #ffffff; + display: flex; + align-items: center; + gap: 3px; + padding: 0 5px; + cursor: pointer; + font-family: var(--font-sans); + font-size: 0.6875rem; + font-weight: 600; +} + +.xp-tab-button.active { + background: linear-gradient(180deg, #1b45a0 0%, #153888 100%); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.6); + border-color: #0b2255; +} + +.tab-win-icon { width: 8px; height: 8px; border-radius: 1px; background: #ffb300; flex-shrink: 0; } +.tab-win-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + +/* Tray */ +.xp-system-tray { + height: 100%; + background: linear-gradient(180deg, #0c3ea6 0%, #104abf 40%, #0c3694 100%); + border-left: 1px solid #1646b9; + border-top: 1px solid #092a7a; + box-shadow: inset 1px 1px 0 #072366; + display: flex; + align-items: center; + gap: 4px; + padding: 0 6px; + color: #ffffff; +} + +.tray-expand { font-size: 0.5rem; background: #1754ce; border-radius: 50%; width: 11px; height: 11px; display: flex; align-items: center; justify-content: center; } +.tray-mini-icon { font-size: 0.625rem; } +.xp-tray-clock { font-family: var(--font-sans); font-size: 0.6875rem; font-weight: 600; margin-left: 2px; } diff --git a/src/frontend/src/app/presentation/features/retro-os/retro-os.component.html b/src/frontend/src/app/presentation/features/retro-os/retro-os.component.html new file mode 100644 index 0000000..b155f20 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/retro-os.component.html @@ -0,0 +1,94 @@ +
+ + Monitor CRT Vintage + + +
+
+ + + + +
+ @for (win of store.openWindows(); track win.id) { + @if (!win.isMinimized) { + + @switch (win.id) { + @case ('games') { + + } + @case ('cv') { + + } + @case ('scores') { + + } + @case ('settings') { + + } + } + + } + } +
+ + + @if (isStartMenuOpen()) { + + } + + +
+ + +
+ + + + +
+ + + +
+ + +
+ @for (win of store.openWindows(); track win.id) { + + } +
+ + +
+ < + 🛡 + 🔊 + 🖧 + {{ currentTime() }} +
+
+
+
+
\ No newline at end of file diff --git a/src/frontend/src/app/presentation/features/retro-os/retro-os.component.ts b/src/frontend/src/app/presentation/features/retro-os/retro-os.component.ts new file mode 100644 index 0000000..731a6e6 --- /dev/null +++ b/src/frontend/src/app/presentation/features/retro-os/retro-os.component.ts @@ -0,0 +1,79 @@ +import { + Component, + ChangeDetectionStrategy, + OnInit, + OnDestroy, + signal, + inject +} from '@angular/core'; +import { RetroOsStore } from '../../../application/stores/retro-os.store'; +import { I18nService } from '../../../application/services/i18n.service'; +import { WindowId, WindowPosition } from '../../../domain/models/retro-os.model'; +import { RetroDesktopIconsComponent } from './components/retro-desktop-icons/retro-desktop-icons.component'; +import { RetroWindowComponent } from './components/retro-window/retro-window.component'; +import { RetroGameCanvasComponent } from './components/retro-game-canvas/retro-game-canvas.component'; +import { RetroCvComponent } from './components/retro-cv/retro-cv.component'; +import { RetroScoresComponent } from './components/retro-scores/retro-scores.component'; +import { RetroSettingsComponent } from './components/retro-settings/retro-settings.component'; +import { RetroStartMenuComponent } from './components/retro-start-menu/retro-start-menu.component'; + +@Component({ + selector: 'app-retro-os', + standalone: true, + imports: [ + RetroDesktopIconsComponent, + RetroWindowComponent, + RetroGameCanvasComponent, + RetroCvComponent, + RetroScoresComponent, + RetroSettingsComponent, + RetroStartMenuComponent + ], + templateUrl: './retro-os.component.html', + styleUrl: './retro-os.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RetroOsComponent implements OnInit, OnDestroy { + protected readonly store = inject(RetroOsStore); + protected readonly i18n = inject(I18nService); + + protected readonly isStartMenuOpen = signal(false); + protected readonly currentTime = signal('12:00'); + + private clockInterval: ReturnType | null = null; + private readonly timeFormatter = new Intl.DateTimeFormat('es-EC', { + hour: '2-digit', + minute: '2-digit', + hour12: true + }); + + ngOnInit(): void { + this.updateClock(); + this.clockInterval = setInterval(() => this.updateClock(), 1000); + } + + private updateClock(): void { + this.currentTime.set(this.timeFormatter.format(new Date())); + } + + toggleStartMenu(event?: MouseEvent): void { + event?.stopPropagation(); + this.isStartMenuOpen.update(v => !v); + } + + closeStartMenu(): void { + if (this.isStartMenuOpen()) { + this.isStartMenuOpen.set(false); + } + } + + onMoveWindow(event: { id: WindowId; position: WindowPosition }): void { + this.store.moveWindow(event.id, event.position); + } + + ngOnDestroy(): void { + if (this.clockInterval !== null) { + clearInterval(this.clockInterval); + } + } +} diff --git a/src/frontend/src/app/presentation/features/technical-profile/technical-profile.component.css b/src/frontend/src/app/presentation/features/technical-profile/technical-profile.component.css new file mode 100644 index 0000000..2f9337c --- /dev/null +++ b/src/frontend/src/app/presentation/features/technical-profile/technical-profile.component.css @@ -0,0 +1,136 @@ +:host { + display: block; + width: 100%; + height: 100%; +} + +.profile-editorial-content { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + gap: var(--space-4); + padding: var(--space-2) 0; + background: transparent; + border: none; + box-shadow: none; + user-select: text; + box-sizing: border-box; +} + +/* Eyebrow */ +.editorial-eyebrow { + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0.12em; + color: var(--zen-accent-kintsugi); + text-transform: uppercase; + display: inline-block; +} + +/* Título Principal H2 */ +.editorial-title { + font-family: var(--font-sans); + font-size: clamp(1.85rem, 3vw, 2.35rem); + font-weight: 800; + color: #ffffff; + line-height: 1.15; + letter-spacing: -0.02em; + margin: 0; +} + +/* Párrafo Lead */ +.editorial-lead { + font-family: var(--font-sans); + font-size: 0.9375rem; + color: var(--zen-text-secondary); + line-height: 1.6; + margin: 0; +} + +.editorial-lead strong { + color: #ffffff; + font-weight: 600; +} + +/* Secciones de Contenido */ +.editorial-section { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +.section-title-row { + display: flex; + align-items: center; + gap: 6px; +} + +.section-bullet { + color: var(--zen-accent-kintsugi); + font-size: 0.875rem; + font-weight: 700; +} + +.editorial-subtitle { + font-family: var(--font-sans); + font-size: 1.15rem; + font-weight: 700; + color: #ffffff; + margin: 0; + letter-spacing: -0.01em; +} + +/* Grid de Categorías Técnicas */ +.skills-grid { + display: flex; + flex-direction: column; + gap: 6px; + padding-left: 14px; +} + +.skill-category { + font-family: var(--font-sans); + font-size: 0.8125rem; + line-height: 1.5; + color: var(--zen-text-secondary); +} + +.cat-label { + font-family: var(--font-sans); + font-weight: 700; + color: #7dcfff; + font-size: 0.8125rem; + margin-right: 6px; +} + +.cat-techs { + color: #c0caf5; +} + +/* Lista de Áreas de Interés */ +.interests-list { + list-style: none; + padding: 0 0 0 14px; + margin: 0; + display: flex; + flex-direction: column; + gap: 6px; +} + +.interest-item { + font-family: var(--font-sans); + font-size: 0.8125rem; + line-height: 1.5; + color: var(--zen-text-secondary); +} + +.interest-key { + font-family: var(--font-sans); + font-weight: 700; + color: #bb9af7; + font-size: 0.8125rem; + margin-right: 6px; +} diff --git a/src/frontend/src/app/presentation/features/technical-profile/technical-profile.component.html b/src/frontend/src/app/presentation/features/technical-profile/technical-profile.component.html new file mode 100644 index 0000000..d1c15b1 --- /dev/null +++ b/src/frontend/src/app/presentation/features/technical-profile/technical-profile.component.html @@ -0,0 +1,74 @@ +
+ + + {{ i18n.t().tpEyebrow }} + + + +

+ {{ i18n.t().tpTitle }} +

+ + +

+ {{ i18n.t().tpLeadStudent }} Escuela Politécnica Nacional (EPN). + {{ i18n.t().tpLeadSpecialization }} {{ i18n.t().tpLeadStack }}{{ i18n.t().tpLeadEnd }} +

+ + +
+
+ ▹ +

{{ i18n.t().tpSkillsSubtitle }}

+
+ +
+ +
+ {{ i18n.t().tpCatBackend }} + .NET 8/9, C#, Clean Architecture, CQRS (MediatR), FluentValidation, REST APIs. +
+ + +
+ {{ i18n.t().tpCatFrontend }} + Angular 16+, Signals reactivos, ChangeDetection OnPush, TypeScript, Canvas 2D. +
+ + +
+ {{ i18n.t().tpCatData }} + MySQL, PostgreSQL, SQLite, Docker, AWS (Academy Graduate), Git/GitHub CI/CD. +
+ + +
+ {{ i18n.t().tpCatSecurity }} + {{ i18n.t().tpCatSecurityDesc }} +
+
+
+ + +
+
+ ▹ +

{{ i18n.t().tpInterestsSubtitle }}

+
+ +
    +
  • + {{ i18n.t().tpInterest1Key }} + {{ i18n.t().tpInterest1Desc }} +
  • +
  • + {{ i18n.t().tpInterest2Key }} + {{ i18n.t().tpInterest2Desc }} +
  • +
  • + {{ i18n.t().tpInterest3Key }} + {{ i18n.t().tpInterest3Desc }} +
  • +
+
+
diff --git a/src/frontend/src/app/presentation/features/technical-profile/technical-profile.component.ts b/src/frontend/src/app/presentation/features/technical-profile/technical-profile.component.ts new file mode 100644 index 0000000..a1e86e3 --- /dev/null +++ b/src/frontend/src/app/presentation/features/technical-profile/technical-profile.component.ts @@ -0,0 +1,13 @@ +import { Component, ChangeDetectionStrategy, inject } from '@angular/core'; +import { I18nService } from '../../../application/services/i18n.service'; + +@Component({ + selector: 'app-technical-profile', + standalone: true, + templateUrl: './technical-profile.component.html', + styleUrl: './technical-profile.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class TechnicalProfileComponent { + protected readonly i18n = inject(I18nService); +} diff --git a/src/frontend/src/app/presentation/features/terminal/terminal.component.css b/src/frontend/src/app/presentation/features/terminal/terminal.component.css new file mode 100644 index 0000000..afb2162 --- /dev/null +++ b/src/frontend/src/app/presentation/features/terminal/terminal.component.css @@ -0,0 +1,208 @@ +:host { + display: block; + width: 100%; + height: 100%; +} + +.arch-terminal-card { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + min-height: 480px; + max-height: 560px; + background-color: #0b0e14; + border: 1px solid var(--zen-border-subtle); + border-radius: var(--zen-radius-sm); + box-shadow: 0 12px 36px rgba(0, 0, 0, 0.65), 0 0 1px 1px rgba(255, 255, 255, 0.04); + overflow: hidden; + font-family: var(--font-mono); + user-select: text; + cursor: text; +} + +/* Barra Superior de Ventana Unix */ +.term-window-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 var(--space-4); + height: 36px; + background-color: #12161f; + border-bottom: 1px solid var(--zen-border-subtle); + user-select: none; + cursor: default; +} + +.term-traffic-lights { + display: flex; + align-items: center; + gap: 6px; + width: 60px; +} + +.light-dot { + width: 11px; + height: 11px; + border-radius: 50%; + display: inline-block; + cursor: pointer; + transition: opacity var(--transition-fast); +} + +.light-dot:hover { + opacity: 0.8; +} + +.dot-close { background-color: #f7768e; } +.dot-min { background-color: #e0af68; } +.dot-max { background-color: #9ece6a; } + +.term-title-block { + display: flex; + align-items: center; + gap: 6px; + font-family: var(--font-sans); + font-size: 0.8125rem; + font-weight: 700; + color: var(--zen-text-secondary); +} + +.arch-icon { + color: #7dcfff; + font-size: 0.8125rem; +} + +.term-header-status { + width: 60px; + display: flex; + justify-content: flex-end; + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; +} + +.git-branch { + color: #565f89; +} + +.branch-name { + color: #bb9af7; +} + +/* Área de Salida de Terminal */ +.term-body { + flex: 1; + padding: var(--space-4); + overflow-y: auto; + overflow-x: hidden; + line-height: 1.6; + font-size: 0.8125rem; + background: radial-gradient(circle at top right, rgba(122, 162, 247, 0.04) 0%, transparent 60%); +} + +.term-banner { + margin: 0 0 var(--space-3) 0; + font-family: inherit; + font-size: 0.75rem; + line-height: 1.35; + color: #7dcfff; + white-space: pre-wrap; + word-break: break-word; +} + +.term-output { + margin: 0 0 var(--space-3) 0; + font-family: inherit; + font-size: 0.8125rem; + color: var(--zen-text-primary); + white-space: pre-wrap; + word-break: break-word; +} + +.term-error { + color: #f7768e; +} + +.term-success { + color: #9ece6a; +} + +.term-ascii { + color: #7aa2f7; + font-family: inherit; +} + +.term-line-input { + display: flex; + align-items: baseline; + gap: 6px; + margin-bottom: var(--space-2); +} + +.typed-command { + color: #c0caf5; + font-weight: 500; +} + +/* Fila Activa del Prompt (Estilo Fish Shell Pastel) */ +.term-prompt-row { + display: flex; + align-items: baseline; + gap: 6px; + position: relative; + margin-top: var(--space-2); +} + +.prompt-user { + color: #7dcfff; + font-weight: 700; +} + +.prompt-path { + color: #bb9af7; + font-weight: 600; +} + +.prompt-symbol { + color: #73daca; + font-weight: 700; +} + +.term-input-wrapper { + flex: 1; + position: relative; + display: flex; + align-items: baseline; + min-width: 0; +} + +.term-input { + width: 100%; + background: transparent; + border: none; + outline: none; + font-family: inherit; + font-size: inherit; + color: #c0caf5; + padding: 0; + margin: 0; + caret-color: var(--zen-accent-kintsugi); + z-index: 2; +} + +.term-ghost-text { + position: absolute; + left: 0; + top: 0; + white-space: pre; + color: #565f89; + font-family: inherit; + font-size: inherit; + pointer-events: none; + z-index: 1; +} + +.ghost-spacer { + visibility: hidden; +} diff --git a/src/frontend/src/app/presentation/features/terminal/terminal.component.html b/src/frontend/src/app/presentation/features/terminal/terminal.component.html new file mode 100644 index 0000000..9773a11 --- /dev/null +++ b/src/frontend/src/app/presentation/features/terminal/terminal.component.html @@ -0,0 +1,67 @@ +
+ +
+ + +
+ 󰣇 + bryan@archlinux: ~ (fish) +
+ +
+ + +
+ @for (entry of entries(); track entry.id) { + @switch (entry.type) { + @case ('banner') { +
{{ entry.content }}
+ } + @case ('input') { +
+ bryan@archlinux + ~ + ❯ + {{ entry.command }} +
+ } + @case ('error') { +
{{ entry.content }}
+ } + @case ('success') { +
{{ entry.content }}
+ } + @case ('ascii') { +
{{ entry.content }}
+ } + @default { +
{{ entry.content }}
+ } + } + } + + +
+ bryan@archlinux + ~ + ❯ + +
+ + + + @if (suggestion(); as suggest) { + + } +
+
+
+
\ No newline at end of file diff --git a/src/frontend/src/app/presentation/features/terminal/terminal.component.ts b/src/frontend/src/app/presentation/features/terminal/terminal.component.ts new file mode 100644 index 0000000..8f0d064 --- /dev/null +++ b/src/frontend/src/app/presentation/features/terminal/terminal.component.ts @@ -0,0 +1,192 @@ +import { + Component, + ChangeDetectionStrategy, + signal, + computed, + inject, + viewChild, + ElementRef, + AfterViewInit, + effect +} from '@angular/core'; +import { TerminalExecutorService } from '../../../application/services/terminal-executor.service'; +import { I18nService } from '../../../application/services/i18n.service'; + +export interface TerminalEntry { + readonly id: string; + readonly type: 'input' | 'output' | 'error' | 'success' | 'banner' | 'ascii'; + readonly command?: string; + readonly content?: string; +} + +@Component({ + selector: 'app-terminal', + standalone: true, + templateUrl: './terminal.component.html', + styleUrl: './terminal.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class TerminalComponent implements AfterViewInit { + private readonly executor = inject(TerminalExecutorService); + protected readonly i18n = inject(I18nService); + + private readonly terminalBodyRef = viewChild>('terminalBody'); + private readonly cmdInputRef = viewChild>('cmdInput'); + + protected readonly currentInput = signal(''); + protected readonly commandHistory = signal([]); + protected readonly historyIndex = signal(-1); + + // Autosugerencia estilo Fish Shell en texto atenuado + protected readonly suggestion = computed(() => + this.executor.getSuggestions(this.currentInput()) + ); + + protected readonly entries = signal([ + { + id: 'init-banner', + type: 'banner', + content: this.i18n.t().terminalBanner + }, + { + id: 'init-hint', + type: 'output', + content: this.i18n.t().terminalHint + } + ]); + + constructor() { + effect(() => { + const t = this.i18n.t(); + this.entries.update(prev => + prev.map(e => { + if (e.id === 'init-banner') { + return { ...e, content: t.terminalBanner }; + } + if (e.id === 'init-hint') { + return { ...e, content: t.terminalHint }; + } + return e; + }) + ); + }); + } + + ngAfterViewInit(): void { + this.scrollToBottom(); + } + + focusInput(): void { + this.cmdInputRef()?.nativeElement.focus(); + } + + onKeyDown(event: KeyboardEvent): void { + // 1. Aceptar autocompletado de Fish con Tab o Flecha Derecha al final + if (event.key === 'Tab' || (event.key === 'ArrowRight' && this.isCursorAtEnd())) { + const suggest = this.suggestion(); + if (suggest) { + event.preventDefault(); + this.currentInput.update(val => val + suggest); + return; + } + } + + // 2. Limpiar con Ctrl + L + if (event.ctrlKey && event.key.toLowerCase() === 'l') { + event.preventDefault(); + this.clear(); + return; + } + + // 3. Historial hacia atrás (Flecha Arriba) + if (event.key === 'ArrowUp') { + event.preventDefault(); + const history = this.commandHistory(); + if (history.length === 0) return; + + const newIndex = Math.min(this.historyIndex() + 1, history.length - 1); + this.historyIndex.set(newIndex); + this.currentInput.set(history[history.length - 1 - newIndex]); + return; + } + + // 4. Historial hacia adelante (Flecha Abajo) + if (event.key === 'ArrowDown') { + event.preventDefault(); + const history = this.commandHistory(); + if (this.historyIndex() <= 0) { + this.historyIndex.set(-1); + this.currentInput.set(''); + return; + } + + const newIndex = this.historyIndex() - 1; + this.historyIndex.set(newIndex); + this.currentInput.set(history[history.length - 1 - newIndex]); + return; + } + } + + onSubmit(event: Event): void { + event.preventDefault(); + const raw = this.currentInput(); + const trimmed = raw.trim(); + + if (!trimmed) { + // Línea vacía con solo prompt + this.entries.update(prev => [ + ...prev, + { id: crypto.randomUUID(), type: 'input', command: '' } + ]); + this.scrollToBottom(); + return; + } + + // Guardar en historial + this.commandHistory.update(prev => [...prev, raw]); + this.historyIndex.set(-1); + + // Entrada del comando + this.entries.update(prev => [ + ...prev, + { id: crypto.randomUUID(), type: 'input', command: raw } + ]); + + // Ejecutar lógica + const result = this.executor.execute(raw); + + if (result.type === 'clear') { + this.entries.set([]); + } else { + const entry: TerminalEntry = { + id: crypto.randomUUID(), + type: result.type, + content: result.content + }; + this.entries.update(prev => [...prev, entry]); + } + + this.currentInput.set(''); + this.scrollToBottom(); + } + + clear(): void { + this.entries.set([]); + this.currentInput.set(''); + } + + private isCursorAtEnd(): boolean { + const input = this.cmdInputRef()?.nativeElement; + if (!input) return true; + return input.selectionStart === input.value.length; + } + + private scrollToBottom(): void { + requestAnimationFrame(() => { + const el = this.terminalBodyRef()?.nativeElement; + if (el) { + el.scrollTop = el.scrollHeight; + } + }); + } +} diff --git a/src/frontend/src/app/presentation/shared/components/.gitkeep b/src/frontend/src/app/presentation/shared/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/app/presentation/shared/pipes/.gitkeep b/src/frontend/src/app/presentation/shared/pipes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/frontend/src/assets/CV Bryan Baño .pdf b/src/frontend/src/assets/CV Bryan Baño .pdf new file mode 100644 index 0000000..7651da2 Binary files /dev/null and b/src/frontend/src/assets/CV Bryan Baño .pdf differ diff --git a/src/frontend/src/assets/images/gengar.jpg b/src/frontend/src/assets/images/gengar.jpg new file mode 100644 index 0000000..cc1be6c Binary files /dev/null and b/src/frontend/src/assets/images/gengar.jpg differ diff --git a/src/frontend/src/assets/images/monitor-full.png b/src/frontend/src/assets/images/monitor-full.png new file mode 100644 index 0000000..bf9853b Binary files /dev/null and b/src/frontend/src/assets/images/monitor-full.png differ diff --git a/src/frontend/src/assets/images/monitor.png b/src/frontend/src/assets/images/monitor.png new file mode 100644 index 0000000..f090387 Binary files /dev/null and b/src/frontend/src/assets/images/monitor.png differ diff --git a/src/frontend/src/assets/images/monitor.webp b/src/frontend/src/assets/images/monitor.webp new file mode 100644 index 0000000..14abd35 Binary files /dev/null and b/src/frontend/src/assets/images/monitor.webp differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/001.jpg b/src/frontend/src/assets/images/thinkpad-sequence/001.jpg new file mode 100644 index 0000000..1fe4396 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/001.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/002.jpg b/src/frontend/src/assets/images/thinkpad-sequence/002.jpg new file mode 100644 index 0000000..b5a3adc Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/002.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/003.jpg b/src/frontend/src/assets/images/thinkpad-sequence/003.jpg new file mode 100644 index 0000000..5790666 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/003.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/004.jpg b/src/frontend/src/assets/images/thinkpad-sequence/004.jpg new file mode 100644 index 0000000..1ae2f04 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/004.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/005.jpg b/src/frontend/src/assets/images/thinkpad-sequence/005.jpg new file mode 100644 index 0000000..d3145ad Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/005.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/006.jpg b/src/frontend/src/assets/images/thinkpad-sequence/006.jpg new file mode 100644 index 0000000..1e3dec2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/006.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/007.jpg b/src/frontend/src/assets/images/thinkpad-sequence/007.jpg new file mode 100644 index 0000000..10e4fb2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/007.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/008.jpg b/src/frontend/src/assets/images/thinkpad-sequence/008.jpg new file mode 100644 index 0000000..4dac970 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/008.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/009.jpg b/src/frontend/src/assets/images/thinkpad-sequence/009.jpg new file mode 100644 index 0000000..8fd5dde Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/009.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/010.jpg b/src/frontend/src/assets/images/thinkpad-sequence/010.jpg new file mode 100644 index 0000000..edfbe17 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/010.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/011.jpg b/src/frontend/src/assets/images/thinkpad-sequence/011.jpg new file mode 100644 index 0000000..ef5ffd8 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/011.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/012.jpg b/src/frontend/src/assets/images/thinkpad-sequence/012.jpg new file mode 100644 index 0000000..684fc0b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/012.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/013.jpg b/src/frontend/src/assets/images/thinkpad-sequence/013.jpg new file mode 100644 index 0000000..684fc0b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/013.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/014.jpg b/src/frontend/src/assets/images/thinkpad-sequence/014.jpg new file mode 100644 index 0000000..b3124b3 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/014.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/015.jpg b/src/frontend/src/assets/images/thinkpad-sequence/015.jpg new file mode 100644 index 0000000..d04ebd4 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/015.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/016.jpg b/src/frontend/src/assets/images/thinkpad-sequence/016.jpg new file mode 100644 index 0000000..27c9b01 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/016.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/017.jpg b/src/frontend/src/assets/images/thinkpad-sequence/017.jpg new file mode 100644 index 0000000..0a1c169 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/017.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/018.jpg b/src/frontend/src/assets/images/thinkpad-sequence/018.jpg new file mode 100644 index 0000000..0a1c169 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/018.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/019.jpg b/src/frontend/src/assets/images/thinkpad-sequence/019.jpg new file mode 100644 index 0000000..5d2706a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/019.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/020.jpg b/src/frontend/src/assets/images/thinkpad-sequence/020.jpg new file mode 100644 index 0000000..df10f4c Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/020.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/021.jpg b/src/frontend/src/assets/images/thinkpad-sequence/021.jpg new file mode 100644 index 0000000..3383170 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/021.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/022.jpg b/src/frontend/src/assets/images/thinkpad-sequence/022.jpg new file mode 100644 index 0000000..6621b64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/022.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/023.jpg b/src/frontend/src/assets/images/thinkpad-sequence/023.jpg new file mode 100644 index 0000000..6621b64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/023.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/024.jpg b/src/frontend/src/assets/images/thinkpad-sequence/024.jpg new file mode 100644 index 0000000..f6eab28 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/024.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/025.jpg b/src/frontend/src/assets/images/thinkpad-sequence/025.jpg new file mode 100644 index 0000000..fce313d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/025.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/026.jpg b/src/frontend/src/assets/images/thinkpad-sequence/026.jpg new file mode 100644 index 0000000..35537fb Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/026.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/027.jpg b/src/frontend/src/assets/images/thinkpad-sequence/027.jpg new file mode 100644 index 0000000..0ebff64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/027.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/028.jpg b/src/frontend/src/assets/images/thinkpad-sequence/028.jpg new file mode 100644 index 0000000..0ebff64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/028.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/029.jpg b/src/frontend/src/assets/images/thinkpad-sequence/029.jpg new file mode 100644 index 0000000..c0d4a8a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/029.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/030.jpg b/src/frontend/src/assets/images/thinkpad-sequence/030.jpg new file mode 100644 index 0000000..68c5ff5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/030.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/031.jpg b/src/frontend/src/assets/images/thinkpad-sequence/031.jpg new file mode 100644 index 0000000..bf15149 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/031.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/032.jpg b/src/frontend/src/assets/images/thinkpad-sequence/032.jpg new file mode 100644 index 0000000..995f54b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/032.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/033.jpg b/src/frontend/src/assets/images/thinkpad-sequence/033.jpg new file mode 100644 index 0000000..995f54b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/033.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/034.jpg b/src/frontend/src/assets/images/thinkpad-sequence/034.jpg new file mode 100644 index 0000000..2225431 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/034.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/035.jpg b/src/frontend/src/assets/images/thinkpad-sequence/035.jpg new file mode 100644 index 0000000..312a2ef Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/035.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/036.jpg b/src/frontend/src/assets/images/thinkpad-sequence/036.jpg new file mode 100644 index 0000000..9fc44a1 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/036.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/037.jpg b/src/frontend/src/assets/images/thinkpad-sequence/037.jpg new file mode 100644 index 0000000..22367cf Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/037.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/038.jpg b/src/frontend/src/assets/images/thinkpad-sequence/038.jpg new file mode 100644 index 0000000..22367cf Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/038.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/039.jpg b/src/frontend/src/assets/images/thinkpad-sequence/039.jpg new file mode 100644 index 0000000..1cba700 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/039.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/040.jpg b/src/frontend/src/assets/images/thinkpad-sequence/040.jpg new file mode 100644 index 0000000..e4ec8d7 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/040.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/041.jpg b/src/frontend/src/assets/images/thinkpad-sequence/041.jpg new file mode 100644 index 0000000..7f75495 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/041.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/042.jpg b/src/frontend/src/assets/images/thinkpad-sequence/042.jpg new file mode 100644 index 0000000..0a562e5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/042.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/043.jpg b/src/frontend/src/assets/images/thinkpad-sequence/043.jpg new file mode 100644 index 0000000..0a562e5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/043.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/044.jpg b/src/frontend/src/assets/images/thinkpad-sequence/044.jpg new file mode 100644 index 0000000..8f1f719 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/044.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/045.jpg b/src/frontend/src/assets/images/thinkpad-sequence/045.jpg new file mode 100644 index 0000000..924aa2f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/045.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/046.jpg b/src/frontend/src/assets/images/thinkpad-sequence/046.jpg new file mode 100644 index 0000000..95e0255 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/046.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/047.jpg b/src/frontend/src/assets/images/thinkpad-sequence/047.jpg new file mode 100644 index 0000000..ca0600e Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/047.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/048.jpg b/src/frontend/src/assets/images/thinkpad-sequence/048.jpg new file mode 100644 index 0000000..ca0600e Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/048.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/049.jpg b/src/frontend/src/assets/images/thinkpad-sequence/049.jpg new file mode 100644 index 0000000..eb403b7 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/049.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/050.jpg b/src/frontend/src/assets/images/thinkpad-sequence/050.jpg new file mode 100644 index 0000000..3c7bb5b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/050.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/051.jpg b/src/frontend/src/assets/images/thinkpad-sequence/051.jpg new file mode 100644 index 0000000..bdb5153 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/051.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/052.jpg b/src/frontend/src/assets/images/thinkpad-sequence/052.jpg new file mode 100644 index 0000000..b668b38 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/052.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/053.jpg b/src/frontend/src/assets/images/thinkpad-sequence/053.jpg new file mode 100644 index 0000000..b668b38 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/053.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/054.jpg b/src/frontend/src/assets/images/thinkpad-sequence/054.jpg new file mode 100644 index 0000000..f452835 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/054.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/055.jpg b/src/frontend/src/assets/images/thinkpad-sequence/055.jpg new file mode 100644 index 0000000..3adacc3 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/055.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/056.jpg b/src/frontend/src/assets/images/thinkpad-sequence/056.jpg new file mode 100644 index 0000000..cbe44d1 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/056.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/057.jpg b/src/frontend/src/assets/images/thinkpad-sequence/057.jpg new file mode 100644 index 0000000..7113d26 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/057.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/058.jpg b/src/frontend/src/assets/images/thinkpad-sequence/058.jpg new file mode 100644 index 0000000..7113d26 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/058.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/059.jpg b/src/frontend/src/assets/images/thinkpad-sequence/059.jpg new file mode 100644 index 0000000..e07cf5f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/059.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/060.jpg b/src/frontend/src/assets/images/thinkpad-sequence/060.jpg new file mode 100644 index 0000000..81f2d30 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/060.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/061.jpg b/src/frontend/src/assets/images/thinkpad-sequence/061.jpg new file mode 100644 index 0000000..b362b30 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/061.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/062.jpg b/src/frontend/src/assets/images/thinkpad-sequence/062.jpg new file mode 100644 index 0000000..235062c Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/062.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/063.jpg b/src/frontend/src/assets/images/thinkpad-sequence/063.jpg new file mode 100644 index 0000000..235062c Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/063.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/064.jpg b/src/frontend/src/assets/images/thinkpad-sequence/064.jpg new file mode 100644 index 0000000..b9f6d6a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/064.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/065.jpg b/src/frontend/src/assets/images/thinkpad-sequence/065.jpg new file mode 100644 index 0000000..91169b8 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/065.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/066.jpg b/src/frontend/src/assets/images/thinkpad-sequence/066.jpg new file mode 100644 index 0000000..0e4ab63 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/066.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/067.jpg b/src/frontend/src/assets/images/thinkpad-sequence/067.jpg new file mode 100644 index 0000000..93b5560 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/067.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/068.jpg b/src/frontend/src/assets/images/thinkpad-sequence/068.jpg new file mode 100644 index 0000000..93b5560 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/068.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/069.jpg b/src/frontend/src/assets/images/thinkpad-sequence/069.jpg new file mode 100644 index 0000000..ba98a31 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/069.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/070.jpg b/src/frontend/src/assets/images/thinkpad-sequence/070.jpg new file mode 100644 index 0000000..34c008d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/070.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/071.jpg b/src/frontend/src/assets/images/thinkpad-sequence/071.jpg new file mode 100644 index 0000000..84ad54a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/071.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/072.jpg b/src/frontend/src/assets/images/thinkpad-sequence/072.jpg new file mode 100644 index 0000000..25bd2ed Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/072.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/073.jpg b/src/frontend/src/assets/images/thinkpad-sequence/073.jpg new file mode 100644 index 0000000..25bd2ed Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/073.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/074.jpg b/src/frontend/src/assets/images/thinkpad-sequence/074.jpg new file mode 100644 index 0000000..9d49580 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/074.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/075.jpg b/src/frontend/src/assets/images/thinkpad-sequence/075.jpg new file mode 100644 index 0000000..314ab33 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/075.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/076.jpg b/src/frontend/src/assets/images/thinkpad-sequence/076.jpg new file mode 100644 index 0000000..fcba31d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/076.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/077.jpg b/src/frontend/src/assets/images/thinkpad-sequence/077.jpg new file mode 100644 index 0000000..382e810 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/077.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/078.jpg b/src/frontend/src/assets/images/thinkpad-sequence/078.jpg new file mode 100644 index 0000000..382e810 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/078.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/079.jpg b/src/frontend/src/assets/images/thinkpad-sequence/079.jpg new file mode 100644 index 0000000..c47b681 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/079.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/080.jpg b/src/frontend/src/assets/images/thinkpad-sequence/080.jpg new file mode 100644 index 0000000..9eb5cee Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/080.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/081.jpg b/src/frontend/src/assets/images/thinkpad-sequence/081.jpg new file mode 100644 index 0000000..a4a174b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/081.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/082.jpg b/src/frontend/src/assets/images/thinkpad-sequence/082.jpg new file mode 100644 index 0000000..b913306 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/082.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/083.jpg b/src/frontend/src/assets/images/thinkpad-sequence/083.jpg new file mode 100644 index 0000000..b913306 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/083.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/084.jpg b/src/frontend/src/assets/images/thinkpad-sequence/084.jpg new file mode 100644 index 0000000..aa052d1 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/084.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/085.jpg b/src/frontend/src/assets/images/thinkpad-sequence/085.jpg new file mode 100644 index 0000000..5250d82 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/085.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/086.jpg b/src/frontend/src/assets/images/thinkpad-sequence/086.jpg new file mode 100644 index 0000000..024fb08 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/086.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/087.jpg b/src/frontend/src/assets/images/thinkpad-sequence/087.jpg new file mode 100644 index 0000000..2486eda Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/087.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/088.jpg b/src/frontend/src/assets/images/thinkpad-sequence/088.jpg new file mode 100644 index 0000000..2486eda Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/088.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/089.jpg b/src/frontend/src/assets/images/thinkpad-sequence/089.jpg new file mode 100644 index 0000000..288d4dd Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/089.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/090.jpg b/src/frontend/src/assets/images/thinkpad-sequence/090.jpg new file mode 100644 index 0000000..a5250f4 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/090.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/091.jpg b/src/frontend/src/assets/images/thinkpad-sequence/091.jpg new file mode 100644 index 0000000..a2ff547 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/091.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/092.jpg b/src/frontend/src/assets/images/thinkpad-sequence/092.jpg new file mode 100644 index 0000000..bb74a94 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/092.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/093.jpg b/src/frontend/src/assets/images/thinkpad-sequence/093.jpg new file mode 100644 index 0000000..bb74a94 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/093.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/094.jpg b/src/frontend/src/assets/images/thinkpad-sequence/094.jpg new file mode 100644 index 0000000..5e5afed Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/094.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/095.jpg b/src/frontend/src/assets/images/thinkpad-sequence/095.jpg new file mode 100644 index 0000000..8370d9d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/095.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/096.jpg b/src/frontend/src/assets/images/thinkpad-sequence/096.jpg new file mode 100644 index 0000000..83550a6 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/096.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/097.jpg b/src/frontend/src/assets/images/thinkpad-sequence/097.jpg new file mode 100644 index 0000000..12cb9fb Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/097.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/098.jpg b/src/frontend/src/assets/images/thinkpad-sequence/098.jpg new file mode 100644 index 0000000..12cb9fb Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/098.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/099.jpg b/src/frontend/src/assets/images/thinkpad-sequence/099.jpg new file mode 100644 index 0000000..291c296 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/099.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/100.jpg b/src/frontend/src/assets/images/thinkpad-sequence/100.jpg new file mode 100644 index 0000000..5171466 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/100.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/101.jpg b/src/frontend/src/assets/images/thinkpad-sequence/101.jpg new file mode 100644 index 0000000..ef2189d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/101.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/102.jpg b/src/frontend/src/assets/images/thinkpad-sequence/102.jpg new file mode 100644 index 0000000..3fb70d9 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/102.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/103.jpg b/src/frontend/src/assets/images/thinkpad-sequence/103.jpg new file mode 100644 index 0000000..3fb70d9 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/103.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/104.jpg b/src/frontend/src/assets/images/thinkpad-sequence/104.jpg new file mode 100644 index 0000000..066a111 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/104.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/105.jpg b/src/frontend/src/assets/images/thinkpad-sequence/105.jpg new file mode 100644 index 0000000..8c6c7a3 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/105.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/106.jpg b/src/frontend/src/assets/images/thinkpad-sequence/106.jpg new file mode 100644 index 0000000..b88ff99 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/106.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/107.jpg b/src/frontend/src/assets/images/thinkpad-sequence/107.jpg new file mode 100644 index 0000000..a56877f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/107.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/108.jpg b/src/frontend/src/assets/images/thinkpad-sequence/108.jpg new file mode 100644 index 0000000..a56877f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/108.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/109.jpg b/src/frontend/src/assets/images/thinkpad-sequence/109.jpg new file mode 100644 index 0000000..ec4be1e Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/109.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/110.jpg b/src/frontend/src/assets/images/thinkpad-sequence/110.jpg new file mode 100644 index 0000000..57b7b73 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/110.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/111.jpg b/src/frontend/src/assets/images/thinkpad-sequence/111.jpg new file mode 100644 index 0000000..d042c01 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/111.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/112.jpg b/src/frontend/src/assets/images/thinkpad-sequence/112.jpg new file mode 100644 index 0000000..3948e70 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/112.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/113.jpg b/src/frontend/src/assets/images/thinkpad-sequence/113.jpg new file mode 100644 index 0000000..3948e70 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/113.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/114.jpg b/src/frontend/src/assets/images/thinkpad-sequence/114.jpg new file mode 100644 index 0000000..c59a715 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/114.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/115.jpg b/src/frontend/src/assets/images/thinkpad-sequence/115.jpg new file mode 100644 index 0000000..1069cda Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/115.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/116.jpg b/src/frontend/src/assets/images/thinkpad-sequence/116.jpg new file mode 100644 index 0000000..1f8a03f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/116.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/117.jpg b/src/frontend/src/assets/images/thinkpad-sequence/117.jpg new file mode 100644 index 0000000..eb5a291 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/117.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/118.jpg b/src/frontend/src/assets/images/thinkpad-sequence/118.jpg new file mode 100644 index 0000000..eb5a291 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/118.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/119.jpg b/src/frontend/src/assets/images/thinkpad-sequence/119.jpg new file mode 100644 index 0000000..6501b46 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/119.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/120.jpg b/src/frontend/src/assets/images/thinkpad-sequence/120.jpg new file mode 100644 index 0000000..90ef155 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/120.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/121.jpg b/src/frontend/src/assets/images/thinkpad-sequence/121.jpg new file mode 100644 index 0000000..bfc1287 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/121.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/122.jpg b/src/frontend/src/assets/images/thinkpad-sequence/122.jpg new file mode 100644 index 0000000..5cab0d0 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/122.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/123.jpg b/src/frontend/src/assets/images/thinkpad-sequence/123.jpg new file mode 100644 index 0000000..5cab0d0 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/123.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/124.jpg b/src/frontend/src/assets/images/thinkpad-sequence/124.jpg new file mode 100644 index 0000000..374e10b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/124.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/125.jpg b/src/frontend/src/assets/images/thinkpad-sequence/125.jpg new file mode 100644 index 0000000..5fac1d5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/125.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/126.jpg b/src/frontend/src/assets/images/thinkpad-sequence/126.jpg new file mode 100644 index 0000000..87b8d22 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/126.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/127.jpg b/src/frontend/src/assets/images/thinkpad-sequence/127.jpg new file mode 100644 index 0000000..bfa933a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/127.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/128.jpg b/src/frontend/src/assets/images/thinkpad-sequence/128.jpg new file mode 100644 index 0000000..bfa933a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/128.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/129.jpg b/src/frontend/src/assets/images/thinkpad-sequence/129.jpg new file mode 100644 index 0000000..606da15 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/129.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/130.jpg b/src/frontend/src/assets/images/thinkpad-sequence/130.jpg new file mode 100644 index 0000000..7be502b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/130.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/131.jpg b/src/frontend/src/assets/images/thinkpad-sequence/131.jpg new file mode 100644 index 0000000..c8df466 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/131.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/132.jpg b/src/frontend/src/assets/images/thinkpad-sequence/132.jpg new file mode 100644 index 0000000..eecb611 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/132.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/133.jpg b/src/frontend/src/assets/images/thinkpad-sequence/133.jpg new file mode 100644 index 0000000..eecb611 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/133.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/134.jpg b/src/frontend/src/assets/images/thinkpad-sequence/134.jpg new file mode 100644 index 0000000..f2dad47 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/134.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/135.jpg b/src/frontend/src/assets/images/thinkpad-sequence/135.jpg new file mode 100644 index 0000000..85697ab Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/135.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/136.jpg b/src/frontend/src/assets/images/thinkpad-sequence/136.jpg new file mode 100644 index 0000000..935fedd Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/136.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/137.jpg b/src/frontend/src/assets/images/thinkpad-sequence/137.jpg new file mode 100644 index 0000000..8b61be2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/137.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/138.jpg b/src/frontend/src/assets/images/thinkpad-sequence/138.jpg new file mode 100644 index 0000000..8b61be2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/138.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/139.jpg b/src/frontend/src/assets/images/thinkpad-sequence/139.jpg new file mode 100644 index 0000000..85ed447 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/139.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/140.jpg b/src/frontend/src/assets/images/thinkpad-sequence/140.jpg new file mode 100644 index 0000000..4c672e0 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/140.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/141.jpg b/src/frontend/src/assets/images/thinkpad-sequence/141.jpg new file mode 100644 index 0000000..a29f1a9 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/141.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/142.jpg b/src/frontend/src/assets/images/thinkpad-sequence/142.jpg new file mode 100644 index 0000000..1298849 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/142.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/143.jpg b/src/frontend/src/assets/images/thinkpad-sequence/143.jpg new file mode 100644 index 0000000..1298849 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/143.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/144.jpg b/src/frontend/src/assets/images/thinkpad-sequence/144.jpg new file mode 100644 index 0000000..c478a9d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/144.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/145.jpg b/src/frontend/src/assets/images/thinkpad-sequence/145.jpg new file mode 100644 index 0000000..218426a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/145.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/146.jpg b/src/frontend/src/assets/images/thinkpad-sequence/146.jpg new file mode 100644 index 0000000..37b0650 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/146.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/147.jpg b/src/frontend/src/assets/images/thinkpad-sequence/147.jpg new file mode 100644 index 0000000..0b00b64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/147.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/148.jpg b/src/frontend/src/assets/images/thinkpad-sequence/148.jpg new file mode 100644 index 0000000..0b00b64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/148.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/149.jpg b/src/frontend/src/assets/images/thinkpad-sequence/149.jpg new file mode 100644 index 0000000..88347df Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/149.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/150.jpg b/src/frontend/src/assets/images/thinkpad-sequence/150.jpg new file mode 100644 index 0000000..632b15f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/150.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/151.jpg b/src/frontend/src/assets/images/thinkpad-sequence/151.jpg new file mode 100644 index 0000000..46119b8 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/151.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/152.jpg b/src/frontend/src/assets/images/thinkpad-sequence/152.jpg new file mode 100644 index 0000000..97f6354 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/152.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/153.jpg b/src/frontend/src/assets/images/thinkpad-sequence/153.jpg new file mode 100644 index 0000000..97f6354 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/153.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/154.jpg b/src/frontend/src/assets/images/thinkpad-sequence/154.jpg new file mode 100644 index 0000000..ed51a36 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/154.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/155.jpg b/src/frontend/src/assets/images/thinkpad-sequence/155.jpg new file mode 100644 index 0000000..8f92486 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/155.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/156.jpg b/src/frontend/src/assets/images/thinkpad-sequence/156.jpg new file mode 100644 index 0000000..8e5bb37 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/156.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/157.jpg b/src/frontend/src/assets/images/thinkpad-sequence/157.jpg new file mode 100644 index 0000000..72a15ef Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/157.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/158.jpg b/src/frontend/src/assets/images/thinkpad-sequence/158.jpg new file mode 100644 index 0000000..72a15ef Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/158.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/159.jpg b/src/frontend/src/assets/images/thinkpad-sequence/159.jpg new file mode 100644 index 0000000..2fd18dc Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/159.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/160.jpg b/src/frontend/src/assets/images/thinkpad-sequence/160.jpg new file mode 100644 index 0000000..4b0471f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/160.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/161.jpg b/src/frontend/src/assets/images/thinkpad-sequence/161.jpg new file mode 100644 index 0000000..0e25332 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/161.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/162.jpg b/src/frontend/src/assets/images/thinkpad-sequence/162.jpg new file mode 100644 index 0000000..5e26fc2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/162.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/163.jpg b/src/frontend/src/assets/images/thinkpad-sequence/163.jpg new file mode 100644 index 0000000..5e26fc2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/163.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/164.jpg b/src/frontend/src/assets/images/thinkpad-sequence/164.jpg new file mode 100644 index 0000000..8d69690 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/164.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/165.jpg b/src/frontend/src/assets/images/thinkpad-sequence/165.jpg new file mode 100644 index 0000000..13574dd Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/165.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/166.jpg b/src/frontend/src/assets/images/thinkpad-sequence/166.jpg new file mode 100644 index 0000000..a961820 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/166.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/167.jpg b/src/frontend/src/assets/images/thinkpad-sequence/167.jpg new file mode 100644 index 0000000..58344ad Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/167.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/168.jpg b/src/frontend/src/assets/images/thinkpad-sequence/168.jpg new file mode 100644 index 0000000..58344ad Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/168.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/169.jpg b/src/frontend/src/assets/images/thinkpad-sequence/169.jpg new file mode 100644 index 0000000..a64a3a9 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/169.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/170.jpg b/src/frontend/src/assets/images/thinkpad-sequence/170.jpg new file mode 100644 index 0000000..2e6a520 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/170.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/171.jpg b/src/frontend/src/assets/images/thinkpad-sequence/171.jpg new file mode 100644 index 0000000..90aed8b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/171.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/172.jpg b/src/frontend/src/assets/images/thinkpad-sequence/172.jpg new file mode 100644 index 0000000..8563cba Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/172.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/173.jpg b/src/frontend/src/assets/images/thinkpad-sequence/173.jpg new file mode 100644 index 0000000..8563cba Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/173.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/174.jpg b/src/frontend/src/assets/images/thinkpad-sequence/174.jpg new file mode 100644 index 0000000..d72651f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/174.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/175.jpg b/src/frontend/src/assets/images/thinkpad-sequence/175.jpg new file mode 100644 index 0000000..878ae22 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/175.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/176.jpg b/src/frontend/src/assets/images/thinkpad-sequence/176.jpg new file mode 100644 index 0000000..9b10379 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/176.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/177.jpg b/src/frontend/src/assets/images/thinkpad-sequence/177.jpg new file mode 100644 index 0000000..fa8dc32 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/177.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/178.jpg b/src/frontend/src/assets/images/thinkpad-sequence/178.jpg new file mode 100644 index 0000000..fa8dc32 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/178.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/179.jpg b/src/frontend/src/assets/images/thinkpad-sequence/179.jpg new file mode 100644 index 0000000..fc2d563 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/179.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/180.jpg b/src/frontend/src/assets/images/thinkpad-sequence/180.jpg new file mode 100644 index 0000000..1c12541 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/180.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/181.jpg b/src/frontend/src/assets/images/thinkpad-sequence/181.jpg new file mode 100644 index 0000000..4d3cb8b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/181.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/182.jpg b/src/frontend/src/assets/images/thinkpad-sequence/182.jpg new file mode 100644 index 0000000..ee16f61 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/182.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/183.jpg b/src/frontend/src/assets/images/thinkpad-sequence/183.jpg new file mode 100644 index 0000000..ee16f61 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/183.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/184.jpg b/src/frontend/src/assets/images/thinkpad-sequence/184.jpg new file mode 100644 index 0000000..a6d6899 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/184.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/185.jpg b/src/frontend/src/assets/images/thinkpad-sequence/185.jpg new file mode 100644 index 0000000..f6b984d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/185.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/186.jpg b/src/frontend/src/assets/images/thinkpad-sequence/186.jpg new file mode 100644 index 0000000..0620171 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/186.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/187.jpg b/src/frontend/src/assets/images/thinkpad-sequence/187.jpg new file mode 100644 index 0000000..b49343d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/187.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/188.jpg b/src/frontend/src/assets/images/thinkpad-sequence/188.jpg new file mode 100644 index 0000000..b49343d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/188.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/189.jpg b/src/frontend/src/assets/images/thinkpad-sequence/189.jpg new file mode 100644 index 0000000..908ce09 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/189.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/190.jpg b/src/frontend/src/assets/images/thinkpad-sequence/190.jpg new file mode 100644 index 0000000..24d71c6 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/190.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/191.jpg b/src/frontend/src/assets/images/thinkpad-sequence/191.jpg new file mode 100644 index 0000000..ce5b97f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/191.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/192.jpg b/src/frontend/src/assets/images/thinkpad-sequence/192.jpg new file mode 100644 index 0000000..2f255be Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/192.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/193.jpg b/src/frontend/src/assets/images/thinkpad-sequence/193.jpg new file mode 100644 index 0000000..2f255be Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/193.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/194.jpg b/src/frontend/src/assets/images/thinkpad-sequence/194.jpg new file mode 100644 index 0000000..964a3a3 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/194.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/195.jpg b/src/frontend/src/assets/images/thinkpad-sequence/195.jpg new file mode 100644 index 0000000..6dd280a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/195.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/196.jpg b/src/frontend/src/assets/images/thinkpad-sequence/196.jpg new file mode 100644 index 0000000..50c0744 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/196.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/197.jpg b/src/frontend/src/assets/images/thinkpad-sequence/197.jpg new file mode 100644 index 0000000..a1ab43d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/197.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/198.jpg b/src/frontend/src/assets/images/thinkpad-sequence/198.jpg new file mode 100644 index 0000000..a1ab43d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/198.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/199.jpg b/src/frontend/src/assets/images/thinkpad-sequence/199.jpg new file mode 100644 index 0000000..8296e66 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/199.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/200.jpg b/src/frontend/src/assets/images/thinkpad-sequence/200.jpg new file mode 100644 index 0000000..3b16784 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/200.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/201.jpg b/src/frontend/src/assets/images/thinkpad-sequence/201.jpg new file mode 100644 index 0000000..f259b43 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/201.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/202.jpg b/src/frontend/src/assets/images/thinkpad-sequence/202.jpg new file mode 100644 index 0000000..ad17a01 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/202.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/203.jpg b/src/frontend/src/assets/images/thinkpad-sequence/203.jpg new file mode 100644 index 0000000..ad17a01 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/203.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/204.jpg b/src/frontend/src/assets/images/thinkpad-sequence/204.jpg new file mode 100644 index 0000000..c452c9a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/204.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/205.jpg b/src/frontend/src/assets/images/thinkpad-sequence/205.jpg new file mode 100644 index 0000000..a83baf7 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/205.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/206.jpg b/src/frontend/src/assets/images/thinkpad-sequence/206.jpg new file mode 100644 index 0000000..fe62206 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/206.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/207.jpg b/src/frontend/src/assets/images/thinkpad-sequence/207.jpg new file mode 100644 index 0000000..be10dd0 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/207.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/208.jpg b/src/frontend/src/assets/images/thinkpad-sequence/208.jpg new file mode 100644 index 0000000..be10dd0 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/208.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/209.jpg b/src/frontend/src/assets/images/thinkpad-sequence/209.jpg new file mode 100644 index 0000000..e2b8219 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/209.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/210.jpg b/src/frontend/src/assets/images/thinkpad-sequence/210.jpg new file mode 100644 index 0000000..c90ef9d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/210.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/211.jpg b/src/frontend/src/assets/images/thinkpad-sequence/211.jpg new file mode 100644 index 0000000..b6ddb57 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/211.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/212.jpg b/src/frontend/src/assets/images/thinkpad-sequence/212.jpg new file mode 100644 index 0000000..30ba939 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/212.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/213.jpg b/src/frontend/src/assets/images/thinkpad-sequence/213.jpg new file mode 100644 index 0000000..30ba939 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/213.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/214.jpg b/src/frontend/src/assets/images/thinkpad-sequence/214.jpg new file mode 100644 index 0000000..b7f52f7 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/214.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/215.jpg b/src/frontend/src/assets/images/thinkpad-sequence/215.jpg new file mode 100644 index 0000000..d0f9974 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/215.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/216.jpg b/src/frontend/src/assets/images/thinkpad-sequence/216.jpg new file mode 100644 index 0000000..6c4c2dd Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/216.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/217.jpg b/src/frontend/src/assets/images/thinkpad-sequence/217.jpg new file mode 100644 index 0000000..31e685e Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/217.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/218.jpg b/src/frontend/src/assets/images/thinkpad-sequence/218.jpg new file mode 100644 index 0000000..31e685e Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/218.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/219.jpg b/src/frontend/src/assets/images/thinkpad-sequence/219.jpg new file mode 100644 index 0000000..915db56 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/219.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/220.jpg b/src/frontend/src/assets/images/thinkpad-sequence/220.jpg new file mode 100644 index 0000000..ae3ac04 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/220.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/221.jpg b/src/frontend/src/assets/images/thinkpad-sequence/221.jpg new file mode 100644 index 0000000..0de0f30 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/221.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/222.jpg b/src/frontend/src/assets/images/thinkpad-sequence/222.jpg new file mode 100644 index 0000000..59a7641 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/222.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/223.jpg b/src/frontend/src/assets/images/thinkpad-sequence/223.jpg new file mode 100644 index 0000000..59a7641 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/223.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/224.jpg b/src/frontend/src/assets/images/thinkpad-sequence/224.jpg new file mode 100644 index 0000000..1c18e80 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/224.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/225.jpg b/src/frontend/src/assets/images/thinkpad-sequence/225.jpg new file mode 100644 index 0000000..76b1d55 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/225.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/226.jpg b/src/frontend/src/assets/images/thinkpad-sequence/226.jpg new file mode 100644 index 0000000..e27d565 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/226.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/227.jpg b/src/frontend/src/assets/images/thinkpad-sequence/227.jpg new file mode 100644 index 0000000..af960f5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/227.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/228.jpg b/src/frontend/src/assets/images/thinkpad-sequence/228.jpg new file mode 100644 index 0000000..af960f5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/228.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/229.jpg b/src/frontend/src/assets/images/thinkpad-sequence/229.jpg new file mode 100644 index 0000000..889537a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/229.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/230.jpg b/src/frontend/src/assets/images/thinkpad-sequence/230.jpg new file mode 100644 index 0000000..2f46fd6 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/230.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/231.jpg b/src/frontend/src/assets/images/thinkpad-sequence/231.jpg new file mode 100644 index 0000000..c9d9026 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/231.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/232.jpg b/src/frontend/src/assets/images/thinkpad-sequence/232.jpg new file mode 100644 index 0000000..5142693 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/232.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/233.jpg b/src/frontend/src/assets/images/thinkpad-sequence/233.jpg new file mode 100644 index 0000000..5142693 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/233.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/234.jpg b/src/frontend/src/assets/images/thinkpad-sequence/234.jpg new file mode 100644 index 0000000..d46b26f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/234.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/235.jpg b/src/frontend/src/assets/images/thinkpad-sequence/235.jpg new file mode 100644 index 0000000..55a4a8b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/235.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/236.jpg b/src/frontend/src/assets/images/thinkpad-sequence/236.jpg new file mode 100644 index 0000000..050c352 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/236.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/237.jpg b/src/frontend/src/assets/images/thinkpad-sequence/237.jpg new file mode 100644 index 0000000..92980a1 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/237.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/238.jpg b/src/frontend/src/assets/images/thinkpad-sequence/238.jpg new file mode 100644 index 0000000..92980a1 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/238.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/239.jpg b/src/frontend/src/assets/images/thinkpad-sequence/239.jpg new file mode 100644 index 0000000..1ccc9ba Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/239.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/240.jpg b/src/frontend/src/assets/images/thinkpad-sequence/240.jpg new file mode 100644 index 0000000..e71e1cb Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/240.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-001.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-001.jpg new file mode 100644 index 0000000..1fe4396 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-001.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-002.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-002.jpg new file mode 100644 index 0000000..b5a3adc Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-002.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-003.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-003.jpg new file mode 100644 index 0000000..5790666 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-003.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-004.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-004.jpg new file mode 100644 index 0000000..1ae2f04 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-004.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-005.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-005.jpg new file mode 100644 index 0000000..d3145ad Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-005.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-006.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-006.jpg new file mode 100644 index 0000000..1e3dec2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-006.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-007.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-007.jpg new file mode 100644 index 0000000..10e4fb2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-007.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-008.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-008.jpg new file mode 100644 index 0000000..4dac970 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-008.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-009.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-009.jpg new file mode 100644 index 0000000..8fd5dde Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-009.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-010.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-010.jpg new file mode 100644 index 0000000..edfbe17 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-010.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-011.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-011.jpg new file mode 100644 index 0000000..ef5ffd8 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-011.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-012.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-012.jpg new file mode 100644 index 0000000..684fc0b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-012.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-013.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-013.jpg new file mode 100644 index 0000000..684fc0b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-013.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-014.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-014.jpg new file mode 100644 index 0000000..b3124b3 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-014.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-015.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-015.jpg new file mode 100644 index 0000000..d04ebd4 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-015.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-016.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-016.jpg new file mode 100644 index 0000000..27c9b01 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-016.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-017.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-017.jpg new file mode 100644 index 0000000..0a1c169 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-017.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-018.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-018.jpg new file mode 100644 index 0000000..0a1c169 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-018.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-019.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-019.jpg new file mode 100644 index 0000000..5d2706a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-019.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-020.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-020.jpg new file mode 100644 index 0000000..df10f4c Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-020.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-021.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-021.jpg new file mode 100644 index 0000000..3383170 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-021.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-022.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-022.jpg new file mode 100644 index 0000000..6621b64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-022.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-023.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-023.jpg new file mode 100644 index 0000000..6621b64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-023.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-024.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-024.jpg new file mode 100644 index 0000000..f6eab28 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-024.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-025.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-025.jpg new file mode 100644 index 0000000..fce313d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-025.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-026.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-026.jpg new file mode 100644 index 0000000..35537fb Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-026.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-027.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-027.jpg new file mode 100644 index 0000000..0ebff64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-027.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-028.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-028.jpg new file mode 100644 index 0000000..0ebff64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-028.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-029.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-029.jpg new file mode 100644 index 0000000..c0d4a8a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-029.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-030.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-030.jpg new file mode 100644 index 0000000..68c5ff5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-030.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-031.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-031.jpg new file mode 100644 index 0000000..bf15149 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-031.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-032.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-032.jpg new file mode 100644 index 0000000..995f54b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-032.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-033.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-033.jpg new file mode 100644 index 0000000..995f54b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-033.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-034.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-034.jpg new file mode 100644 index 0000000..2225431 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-034.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-035.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-035.jpg new file mode 100644 index 0000000..312a2ef Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-035.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-036.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-036.jpg new file mode 100644 index 0000000..9fc44a1 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-036.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-037.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-037.jpg new file mode 100644 index 0000000..22367cf Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-037.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-038.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-038.jpg new file mode 100644 index 0000000..22367cf Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-038.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-039.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-039.jpg new file mode 100644 index 0000000..1cba700 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-039.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-040.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-040.jpg new file mode 100644 index 0000000..e4ec8d7 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-040.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-041.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-041.jpg new file mode 100644 index 0000000..7f75495 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-041.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-042.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-042.jpg new file mode 100644 index 0000000..0a562e5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-042.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-043.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-043.jpg new file mode 100644 index 0000000..0a562e5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-043.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-044.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-044.jpg new file mode 100644 index 0000000..8f1f719 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-044.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-045.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-045.jpg new file mode 100644 index 0000000..924aa2f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-045.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-046.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-046.jpg new file mode 100644 index 0000000..95e0255 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-046.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-047.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-047.jpg new file mode 100644 index 0000000..ca0600e Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-047.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-048.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-048.jpg new file mode 100644 index 0000000..ca0600e Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-048.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-049.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-049.jpg new file mode 100644 index 0000000..eb403b7 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-049.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-050.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-050.jpg new file mode 100644 index 0000000..3c7bb5b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-050.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-051.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-051.jpg new file mode 100644 index 0000000..bdb5153 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-051.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-052.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-052.jpg new file mode 100644 index 0000000..b668b38 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-052.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-053.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-053.jpg new file mode 100644 index 0000000..b668b38 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-053.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-054.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-054.jpg new file mode 100644 index 0000000..f452835 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-054.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-055.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-055.jpg new file mode 100644 index 0000000..3adacc3 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-055.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-056.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-056.jpg new file mode 100644 index 0000000..cbe44d1 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-056.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-057.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-057.jpg new file mode 100644 index 0000000..7113d26 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-057.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-058.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-058.jpg new file mode 100644 index 0000000..7113d26 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-058.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-059.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-059.jpg new file mode 100644 index 0000000..e07cf5f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-059.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-060.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-060.jpg new file mode 100644 index 0000000..81f2d30 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-060.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-061.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-061.jpg new file mode 100644 index 0000000..b362b30 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-061.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-062.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-062.jpg new file mode 100644 index 0000000..235062c Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-062.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-063.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-063.jpg new file mode 100644 index 0000000..235062c Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-063.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-064.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-064.jpg new file mode 100644 index 0000000..b9f6d6a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-064.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-065.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-065.jpg new file mode 100644 index 0000000..91169b8 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-065.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-066.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-066.jpg new file mode 100644 index 0000000..0e4ab63 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-066.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-067.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-067.jpg new file mode 100644 index 0000000..93b5560 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-067.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-068.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-068.jpg new file mode 100644 index 0000000..93b5560 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-068.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-069.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-069.jpg new file mode 100644 index 0000000..ba98a31 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-069.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-070.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-070.jpg new file mode 100644 index 0000000..34c008d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-070.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-071.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-071.jpg new file mode 100644 index 0000000..84ad54a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-071.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-072.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-072.jpg new file mode 100644 index 0000000..25bd2ed Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-072.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-073.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-073.jpg new file mode 100644 index 0000000..25bd2ed Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-073.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-074.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-074.jpg new file mode 100644 index 0000000..9d49580 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-074.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-075.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-075.jpg new file mode 100644 index 0000000..314ab33 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-075.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-076.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-076.jpg new file mode 100644 index 0000000..fcba31d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-076.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-077.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-077.jpg new file mode 100644 index 0000000..382e810 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-077.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-078.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-078.jpg new file mode 100644 index 0000000..382e810 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-078.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-079.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-079.jpg new file mode 100644 index 0000000..c47b681 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-079.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-080.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-080.jpg new file mode 100644 index 0000000..9eb5cee Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-080.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-081.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-081.jpg new file mode 100644 index 0000000..a4a174b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-081.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-082.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-082.jpg new file mode 100644 index 0000000..b913306 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-082.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-083.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-083.jpg new file mode 100644 index 0000000..b913306 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-083.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-084.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-084.jpg new file mode 100644 index 0000000..aa052d1 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-084.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-085.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-085.jpg new file mode 100644 index 0000000..5250d82 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-085.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-086.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-086.jpg new file mode 100644 index 0000000..024fb08 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-086.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-087.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-087.jpg new file mode 100644 index 0000000..2486eda Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-087.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-088.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-088.jpg new file mode 100644 index 0000000..2486eda Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-088.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-089.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-089.jpg new file mode 100644 index 0000000..288d4dd Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-089.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-090.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-090.jpg new file mode 100644 index 0000000..a5250f4 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-090.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-091.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-091.jpg new file mode 100644 index 0000000..a2ff547 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-091.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-092.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-092.jpg new file mode 100644 index 0000000..bb74a94 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-092.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-093.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-093.jpg new file mode 100644 index 0000000..bb74a94 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-093.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-094.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-094.jpg new file mode 100644 index 0000000..5e5afed Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-094.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-095.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-095.jpg new file mode 100644 index 0000000..8370d9d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-095.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-096.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-096.jpg new file mode 100644 index 0000000..83550a6 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-096.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-097.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-097.jpg new file mode 100644 index 0000000..12cb9fb Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-097.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-098.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-098.jpg new file mode 100644 index 0000000..12cb9fb Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-098.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-099.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-099.jpg new file mode 100644 index 0000000..291c296 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-099.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-100.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-100.jpg new file mode 100644 index 0000000..5171466 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-100.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-101.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-101.jpg new file mode 100644 index 0000000..ef2189d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-101.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-102.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-102.jpg new file mode 100644 index 0000000..3fb70d9 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-102.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-103.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-103.jpg new file mode 100644 index 0000000..3fb70d9 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-103.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-104.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-104.jpg new file mode 100644 index 0000000..066a111 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-104.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-105.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-105.jpg new file mode 100644 index 0000000..8c6c7a3 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-105.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-106.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-106.jpg new file mode 100644 index 0000000..b88ff99 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-106.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-107.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-107.jpg new file mode 100644 index 0000000..a56877f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-107.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-108.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-108.jpg new file mode 100644 index 0000000..a56877f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-108.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-109.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-109.jpg new file mode 100644 index 0000000..ec4be1e Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-109.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-110.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-110.jpg new file mode 100644 index 0000000..57b7b73 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-110.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-111.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-111.jpg new file mode 100644 index 0000000..d042c01 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-111.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-112.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-112.jpg new file mode 100644 index 0000000..3948e70 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-112.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-113.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-113.jpg new file mode 100644 index 0000000..3948e70 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-113.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-114.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-114.jpg new file mode 100644 index 0000000..c59a715 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-114.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-115.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-115.jpg new file mode 100644 index 0000000..1069cda Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-115.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-116.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-116.jpg new file mode 100644 index 0000000..1f8a03f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-116.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-117.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-117.jpg new file mode 100644 index 0000000..eb5a291 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-117.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-118.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-118.jpg new file mode 100644 index 0000000..eb5a291 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-118.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-119.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-119.jpg new file mode 100644 index 0000000..6501b46 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-119.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-120.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-120.jpg new file mode 100644 index 0000000..90ef155 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-120.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-121.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-121.jpg new file mode 100644 index 0000000..bfc1287 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-121.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-122.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-122.jpg new file mode 100644 index 0000000..5cab0d0 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-122.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-123.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-123.jpg new file mode 100644 index 0000000..5cab0d0 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-123.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-124.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-124.jpg new file mode 100644 index 0000000..374e10b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-124.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-125.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-125.jpg new file mode 100644 index 0000000..5fac1d5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-125.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-126.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-126.jpg new file mode 100644 index 0000000..87b8d22 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-126.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-127.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-127.jpg new file mode 100644 index 0000000..bfa933a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-127.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-128.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-128.jpg new file mode 100644 index 0000000..bfa933a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-128.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-129.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-129.jpg new file mode 100644 index 0000000..606da15 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-129.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-130.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-130.jpg new file mode 100644 index 0000000..7be502b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-130.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-131.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-131.jpg new file mode 100644 index 0000000..c8df466 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-131.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-132.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-132.jpg new file mode 100644 index 0000000..eecb611 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-132.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-133.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-133.jpg new file mode 100644 index 0000000..eecb611 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-133.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-134.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-134.jpg new file mode 100644 index 0000000..f2dad47 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-134.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-135.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-135.jpg new file mode 100644 index 0000000..85697ab Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-135.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-136.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-136.jpg new file mode 100644 index 0000000..935fedd Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-136.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-137.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-137.jpg new file mode 100644 index 0000000..8b61be2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-137.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-138.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-138.jpg new file mode 100644 index 0000000..8b61be2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-138.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-139.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-139.jpg new file mode 100644 index 0000000..85ed447 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-139.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-140.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-140.jpg new file mode 100644 index 0000000..4c672e0 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-140.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-141.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-141.jpg new file mode 100644 index 0000000..a29f1a9 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-141.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-142.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-142.jpg new file mode 100644 index 0000000..1298849 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-142.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-143.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-143.jpg new file mode 100644 index 0000000..1298849 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-143.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-144.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-144.jpg new file mode 100644 index 0000000..c478a9d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-144.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-145.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-145.jpg new file mode 100644 index 0000000..218426a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-145.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-146.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-146.jpg new file mode 100644 index 0000000..37b0650 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-146.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-147.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-147.jpg new file mode 100644 index 0000000..0b00b64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-147.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-148.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-148.jpg new file mode 100644 index 0000000..0b00b64 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-148.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-149.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-149.jpg new file mode 100644 index 0000000..88347df Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-149.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-150.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-150.jpg new file mode 100644 index 0000000..632b15f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-150.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-151.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-151.jpg new file mode 100644 index 0000000..46119b8 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-151.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-152.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-152.jpg new file mode 100644 index 0000000..97f6354 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-152.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-153.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-153.jpg new file mode 100644 index 0000000..97f6354 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-153.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-154.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-154.jpg new file mode 100644 index 0000000..ed51a36 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-154.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-155.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-155.jpg new file mode 100644 index 0000000..8f92486 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-155.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-156.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-156.jpg new file mode 100644 index 0000000..8e5bb37 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-156.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-157.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-157.jpg new file mode 100644 index 0000000..72a15ef Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-157.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-158.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-158.jpg new file mode 100644 index 0000000..72a15ef Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-158.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-159.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-159.jpg new file mode 100644 index 0000000..2fd18dc Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-159.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-160.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-160.jpg new file mode 100644 index 0000000..4b0471f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-160.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-161.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-161.jpg new file mode 100644 index 0000000..0e25332 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-161.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-162.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-162.jpg new file mode 100644 index 0000000..5e26fc2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-162.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-163.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-163.jpg new file mode 100644 index 0000000..5e26fc2 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-163.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-164.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-164.jpg new file mode 100644 index 0000000..8d69690 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-164.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-165.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-165.jpg new file mode 100644 index 0000000..13574dd Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-165.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-166.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-166.jpg new file mode 100644 index 0000000..a961820 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-166.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-167.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-167.jpg new file mode 100644 index 0000000..58344ad Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-167.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-168.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-168.jpg new file mode 100644 index 0000000..58344ad Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-168.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-169.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-169.jpg new file mode 100644 index 0000000..a64a3a9 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-169.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-170.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-170.jpg new file mode 100644 index 0000000..2e6a520 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-170.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-171.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-171.jpg new file mode 100644 index 0000000..90aed8b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-171.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-172.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-172.jpg new file mode 100644 index 0000000..8563cba Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-172.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-173.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-173.jpg new file mode 100644 index 0000000..8563cba Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-173.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-174.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-174.jpg new file mode 100644 index 0000000..d72651f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-174.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-175.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-175.jpg new file mode 100644 index 0000000..878ae22 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-175.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-176.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-176.jpg new file mode 100644 index 0000000..9b10379 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-176.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-177.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-177.jpg new file mode 100644 index 0000000..fa8dc32 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-177.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-178.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-178.jpg new file mode 100644 index 0000000..fa8dc32 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-178.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-179.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-179.jpg new file mode 100644 index 0000000..fc2d563 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-179.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-180.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-180.jpg new file mode 100644 index 0000000..1c12541 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-180.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-181.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-181.jpg new file mode 100644 index 0000000..4d3cb8b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-181.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-182.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-182.jpg new file mode 100644 index 0000000..ee16f61 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-182.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-183.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-183.jpg new file mode 100644 index 0000000..ee16f61 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-183.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-184.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-184.jpg new file mode 100644 index 0000000..a6d6899 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-184.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-185.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-185.jpg new file mode 100644 index 0000000..f6b984d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-185.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-186.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-186.jpg new file mode 100644 index 0000000..0620171 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-186.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-187.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-187.jpg new file mode 100644 index 0000000..b49343d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-187.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-188.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-188.jpg new file mode 100644 index 0000000..b49343d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-188.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-189.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-189.jpg new file mode 100644 index 0000000..908ce09 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-189.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-190.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-190.jpg new file mode 100644 index 0000000..24d71c6 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-190.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-191.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-191.jpg new file mode 100644 index 0000000..ce5b97f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-191.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-192.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-192.jpg new file mode 100644 index 0000000..2f255be Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-192.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-193.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-193.jpg new file mode 100644 index 0000000..2f255be Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-193.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-194.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-194.jpg new file mode 100644 index 0000000..964a3a3 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-194.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-195.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-195.jpg new file mode 100644 index 0000000..6dd280a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-195.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-196.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-196.jpg new file mode 100644 index 0000000..50c0744 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-196.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-197.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-197.jpg new file mode 100644 index 0000000..a1ab43d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-197.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-198.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-198.jpg new file mode 100644 index 0000000..a1ab43d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-198.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-199.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-199.jpg new file mode 100644 index 0000000..8296e66 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-199.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-200.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-200.jpg new file mode 100644 index 0000000..3b16784 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-200.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-201.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-201.jpg new file mode 100644 index 0000000..f259b43 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-201.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-202.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-202.jpg new file mode 100644 index 0000000..ad17a01 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-202.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-203.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-203.jpg new file mode 100644 index 0000000..ad17a01 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-203.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-204.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-204.jpg new file mode 100644 index 0000000..c452c9a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-204.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-205.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-205.jpg new file mode 100644 index 0000000..a83baf7 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-205.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-206.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-206.jpg new file mode 100644 index 0000000..fe62206 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-206.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-207.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-207.jpg new file mode 100644 index 0000000..be10dd0 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-207.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-208.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-208.jpg new file mode 100644 index 0000000..be10dd0 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-208.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-209.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-209.jpg new file mode 100644 index 0000000..e2b8219 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-209.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-210.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-210.jpg new file mode 100644 index 0000000..c90ef9d Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-210.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-211.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-211.jpg new file mode 100644 index 0000000..b6ddb57 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-211.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-212.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-212.jpg new file mode 100644 index 0000000..30ba939 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-212.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-213.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-213.jpg new file mode 100644 index 0000000..30ba939 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-213.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-214.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-214.jpg new file mode 100644 index 0000000..b7f52f7 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-214.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-215.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-215.jpg new file mode 100644 index 0000000..d0f9974 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-215.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-216.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-216.jpg new file mode 100644 index 0000000..6c4c2dd Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-216.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-217.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-217.jpg new file mode 100644 index 0000000..31e685e Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-217.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-218.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-218.jpg new file mode 100644 index 0000000..31e685e Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-218.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-219.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-219.jpg new file mode 100644 index 0000000..915db56 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-219.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-220.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-220.jpg new file mode 100644 index 0000000..ae3ac04 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-220.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-221.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-221.jpg new file mode 100644 index 0000000..0de0f30 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-221.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-222.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-222.jpg new file mode 100644 index 0000000..59a7641 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-222.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-223.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-223.jpg new file mode 100644 index 0000000..59a7641 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-223.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-224.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-224.jpg new file mode 100644 index 0000000..1c18e80 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-224.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-225.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-225.jpg new file mode 100644 index 0000000..76b1d55 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-225.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-226.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-226.jpg new file mode 100644 index 0000000..e27d565 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-226.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-227.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-227.jpg new file mode 100644 index 0000000..af960f5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-227.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-228.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-228.jpg new file mode 100644 index 0000000..af960f5 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-228.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-229.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-229.jpg new file mode 100644 index 0000000..889537a Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-229.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-230.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-230.jpg new file mode 100644 index 0000000..2f46fd6 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-230.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-231.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-231.jpg new file mode 100644 index 0000000..c9d9026 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-231.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-232.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-232.jpg new file mode 100644 index 0000000..5142693 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-232.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-233.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-233.jpg new file mode 100644 index 0000000..5142693 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-233.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-234.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-234.jpg new file mode 100644 index 0000000..d46b26f Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-234.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-235.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-235.jpg new file mode 100644 index 0000000..55a4a8b Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-235.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-236.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-236.jpg new file mode 100644 index 0000000..050c352 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-236.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-237.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-237.jpg new file mode 100644 index 0000000..92980a1 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-237.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-238.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-238.jpg new file mode 100644 index 0000000..92980a1 Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-238.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-239.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-239.jpg new file mode 100644 index 0000000..1ccc9ba Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-239.jpg differ diff --git a/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-240.jpg b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-240.jpg new file mode 100644 index 0000000..e71e1cb Binary files /dev/null and b/src/frontend/src/assets/images/thinkpad-sequence/ezgif-frame-240.jpg differ diff --git a/src/frontend/src/assets/images/win7-wallpaper.jpg b/src/frontend/src/assets/images/win7-wallpaper.jpg new file mode 100644 index 0000000..a121afe Binary files /dev/null and b/src/frontend/src/assets/images/win7-wallpaper.jpg differ diff --git a/src/frontend/src/assets/images/xp.jpg b/src/frontend/src/assets/images/xp.jpg new file mode 100644 index 0000000..9f3f056 Binary files /dev/null and b/src/frontend/src/assets/images/xp.jpg differ diff --git a/src/frontend/src/index.html b/src/frontend/src/index.html new file mode 100644 index 0000000..498c1b7 --- /dev/null +++ b/src/frontend/src/index.html @@ -0,0 +1,18 @@ + + + + + Bryan // Portafolio Profesional Interactivo + + + + + + + + + + + + + diff --git a/src/frontend/src/main.ts b/src/frontend/src/main.ts new file mode 100644 index 0000000..5df75f9 --- /dev/null +++ b/src/frontend/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { App } from './app/app'; + +bootstrapApplication(App, appConfig) + .catch((err) => console.error(err)); diff --git a/src/frontend/src/styles.css b/src/frontend/src/styles.css new file mode 100644 index 0000000..9ccee5a --- /dev/null +++ b/src/frontend/src/styles.css @@ -0,0 +1,175 @@ +/* ============================================================================== + TOKENS VISUALES GLOBALES: ZEN JAPONÉS + DARK + RETRO (portfolio-ui-design) + ============================================================================== */ + +:root { + /* Tipografía */ + --font-sans: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', monospace; + --font-retro: 'VT323', monospace; + + /* Espaciado (Grid de 8px) */ + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-8: 32px; + --space-10: 40px; + --space-12: 48px; + --space-16: 64px; + + /* Bordes & Curvaturas (Nítidas estilo Zen/Retro) */ + --zen-radius-xs: 2px; + --zen-radius-sm: 4px; + --zen-radius-md: 6px; + + /* Elevaciones & Sombras */ + --zen-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.4); + --zen-shadow-md: 0 4px 16px rgba(0, 0, 0, 0.6); + --zen-shadow-window: 0 10px 30px rgba(0, 0, 0, 0.8), 0 0 1px 1px rgba(255, 255, 255, 0.05); + + /* Transiciones */ + --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-normal: 250ms cubic-bezier(0.4, 0, 0.2, 1); +} + +/* ============================================================================== + TEMA ÚNICO: ZEN DARK (Sumi-e & Kintsugi Gold) + ============================================================================== */ + +:root, +[data-theme='zen-dark'] { + --zen-bg-abyss: #07080b; + --zen-bg-base: #0c0e14; + --zen-bg-surface: #131720; + --zen-bg-elevated: #1a202c; + --zen-bg-overlay: rgba(12, 14, 20, 0.85); + + --zen-border-subtle: #1e2533; + --zen-border-focus: #3d4a63; + --zen-border-kintsugi: #c59b27; + + --zen-text-primary: #f0f3f8; + --zen-text-secondary: #9aa5b8; + --zen-text-muted: #5e697d; + --zen-text-gold: #e2b94a; + + --zen-accent-kintsugi: #c59b27; + --zen-accent-matcha: #4e9b6f; + --zen-accent-sakura: #d96f88; + --zen-accent-amber: #e69d45; + --zen-accent-cyan: #38bdf8; + + --term-bg: #090b0e; + --term-prompt: #4e9b6f; + --term-text: #d8dee9; + --term-cursor: #e2b94a; +} + +/* Tema 2: Retro CRT Ámbar (Estación de trabajo vintage) */ +[data-theme='retro-amber'] { + --zen-bg-abyss: #060401; + --zen-bg-base: #0a0702; + --zen-bg-surface: #140f06; + --zen-bg-elevated: #1e160a; + --zen-border-subtle: #2d1e0a; + --zen-border-focus: #523814; + --zen-border-kintsugi: #ffb833; + --zen-text-primary: #ffb833; + --zen-text-secondary: #cc8e20; + --zen-text-muted: #805710; + --zen-accent-kintsugi: #ffb833; + --zen-accent-matcha: #ffb833; + --zen-accent-amber: #ffb833; + --term-bg: #0d0903; + --term-prompt: #ffb833; + --term-text: #ffcf70; + --term-cursor: #ffb833; +} + +/* Tema 3: Matrix / Cyber-Zen Verde */ +[data-theme='matrix-green'] { + --zen-bg-abyss: #020603; + --zen-bg-base: #040a06; + --zen-bg-surface: #0a140d; + --zen-bg-elevated: #102115; + --zen-border-subtle: #122819; + --zen-border-focus: #1f452b; + --zen-border-kintsugi: #33ff77; + --zen-text-primary: #33ff77; + --zen-text-secondary: #22aa4e; + --zen-text-muted: #15662f; + --zen-accent-kintsugi: #33ff77; + --zen-accent-matcha: #33ff77; + --zen-accent-amber: #66ff99; + --term-bg: #040a06; + --term-prompt: #33ff77; + --term-text: #66ff99; + --term-cursor: #33ff77; +} + +/* ============================================================================== + RESET Y ESTILOS BASE + ============================================================================== */ + +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, body { + width: 100%; + min-height: 100vh; + background-color: var(--zen-bg-base); + color: var(--zen-text-primary); + font-family: var(--font-sans); + font-size: 16px; + line-height: 1.6; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + overflow-x: clip; +} + +body, button, input, select, textarea { + font-family: var(--font-sans); +} + +/* Escala jerárquica de títulos normalizados con fuente única */ +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-sans); + color: var(--zen-text-primary); + letter-spacing: -0.02em; +} + +h1 { font-size: clamp(2.25rem, 4vw, 3.25rem); font-weight: 800; line-height: 1.15; } +h2 { font-size: clamp(1.75rem, 3vw, 2.35rem); font-weight: 800; line-height: 1.2; } +h3 { font-size: clamp(1.25rem, 2vw, 1.5rem); font-weight: 700; line-height: 1.3; } +h4 { font-size: 1.125rem; font-weight: 700; line-height: 1.4; } + +/* Scrollbar minimalista Zen */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--zen-bg-base); +} + +::-webkit-scrollbar-thumb { + background: var(--zen-border-subtle); + border-radius: var(--zen-radius-xs); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--zen-border-focus); +} + +/* Selección de texto kintsugi */ +::selection { + background-color: var(--zen-accent-kintsugi); + color: #000000; +} diff --git a/src/frontend/tsconfig.app.json b/src/frontend/tsconfig.app.json new file mode 100644 index 0000000..264f459 --- /dev/null +++ b/src/frontend/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.spec.ts" + ] +} diff --git a/src/frontend/tsconfig.json b/src/frontend/tsconfig.json new file mode 100644 index 0000000..e4955f2 --- /dev/null +++ b/src/frontend/tsconfig.json @@ -0,0 +1,34 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "compileOnSave": false, + "compilerOptions": { + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES2022", + "module": "preserve" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, + "strictTemplates": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/src/frontend/tsconfig.spec.json b/src/frontend/tsconfig.spec.json new file mode 100644 index 0000000..940b30a --- /dev/null +++ b/src/frontend/tsconfig.spec.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.d.ts", + "src/**/*.spec.ts" + ] +}