From e70ecd3a5139a22919147d805ca93dad1d4ff6e4 Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 03:13:48 +0000 Subject: [PATCH] Implement next scheduled change feature with upcoming actions list - Add schedule calculation utilities with monthly adjustments - Create time formatting utilities for user-friendly display - Add /api/schedule endpoint for schedule data - Enhance /api/state endpoint to include schedule information - Update frontend to display next scheduled change with time - Add upcoming actions list showing all remaining changes for the day - Handle edge cases like day boundaries and tomorrow's schedule --- src/app/api/schedule-utils.js | 159 +++++++++++++++ src/app/api/schedule/route.js | 26 +++ src/app/api/state/route.js | 21 +- src/app/page.js | 81 +++++++- src/app/utils/time-formatting.js | 75 +++++++ yarn.lock | 324 +------------------------------ 6 files changed, 356 insertions(+), 330 deletions(-) create mode 100644 src/app/api/schedule-utils.js create mode 100644 src/app/api/schedule/route.js create mode 100644 src/app/utils/time-formatting.js diff --git a/src/app/api/schedule-utils.js b/src/app/api/schedule-utils.js new file mode 100644 index 0000000..30271c3 --- /dev/null +++ b/src/app/api/schedule-utils.js @@ -0,0 +1,159 @@ +import { schedule, monthAdjustments } from '../../../config'; + +/** + * Apply monthly adjustments to a schedule entry + * @param {Object} entry - Schedule entry with hour, minute, mode + * @param {number} month - Month (0-11) + * @returns {Object} Adjusted schedule entry with adjustedHour and adjustedMinute + */ +export function applyMonthlyAdjustment(entry, month) { + const monthAdjustment = monthAdjustments[month] || 1; + const totalMinutes = (entry.hour * 60 + entry.minute) * monthAdjustment; + const adjustedHour = Math.floor(totalMinutes / 60); + const adjustedMinute = Math.floor(totalMinutes % 60); + + return { + ...entry, + adjustedHour, + adjustedMinute, + monthAdjustment + }; +} + +/** + * Get the adjusted schedule for a given date + * @param {Date} date - Date to get schedule for + * @returns {Array} Array of adjusted schedule entries + */ +export function getAdjustedSchedule(date = new Date()) { + const month = date.getMonth(); + return schedule.map(entry => applyMonthlyAdjustment(entry, month)); +} + +/** + * Find the next scheduled change from a given time + * @param {Date} currentTime - Current time to calculate from + * @returns {Object|null} Next schedule change or null if no more changes today + */ +export function getNextScheduledChange(currentTime = new Date()) { + const adjustedSchedule = getAdjustedSchedule(currentTime); + const currentHour = currentTime.getHours(); + const currentMinute = currentTime.getMinutes(); + + // Find the next schedule entry that hasn't occurred yet today + for (let entry of adjustedSchedule) { + const { adjustedHour, adjustedMinute } = entry; + + // Check if this schedule time is in the future + if (adjustedHour > currentHour || (adjustedHour === currentHour && adjustedMinute > currentMinute)) { + // Calculate time until this change + const changeTime = new Date(currentTime); + changeTime.setHours(adjustedHour, adjustedMinute, 0, 0); + + const timeUntilChange = changeTime.getTime() - currentTime.getTime(); + const minutesUntil = Math.floor(timeUntilChange / (1000 * 60)); + const hoursUntil = Math.floor(minutesUntil / 60); + const remainingMinutes = minutesUntil % 60; + + return { + ...entry, + changeTime, + timeUntilChange, + minutesUntil, + hoursUntil, + remainingMinutes, + isToday: true + }; + } + } + + // If no more changes today, find the first change tomorrow + const tomorrow = new Date(currentTime); + tomorrow.setDate(tomorrow.getDate() + 1); + tomorrow.setHours(0, 0, 0, 0); + + const tomorrowSchedule = getAdjustedSchedule(tomorrow); + if (tomorrowSchedule.length > 0) { + const firstEntry = tomorrowSchedule[0]; + const changeTime = new Date(tomorrow); + changeTime.setHours(firstEntry.adjustedHour, firstEntry.adjustedMinute, 0, 0); + + const timeUntilChange = changeTime.getTime() - currentTime.getTime(); + const minutesUntil = Math.floor(timeUntilChange / (1000 * 60)); + const hoursUntil = Math.floor(minutesUntil / 60); + const remainingMinutes = minutesUntil % 60; + + return { + ...firstEntry, + changeTime, + timeUntilChange, + minutesUntil, + hoursUntil, + remainingMinutes, + isToday: false + }; + } + + return null; +} + +/** + * Get all upcoming schedule changes for the current day + * @param {Date} currentTime - Current time to calculate from + * @returns {Array} Array of upcoming schedule changes + */ +export function getUpcomingChanges(currentTime = new Date()) { + const adjustedSchedule = getAdjustedSchedule(currentTime); + const currentHour = currentTime.getHours(); + const currentMinute = currentTime.getMinutes(); + + return adjustedSchedule + .filter(entry => { + const { adjustedHour, adjustedMinute } = entry; + return adjustedHour > currentHour || (adjustedHour === currentHour && adjustedMinute > currentMinute); + }) + .map(entry => { + const changeTime = new Date(currentTime); + changeTime.setHours(entry.adjustedHour, entry.adjustedMinute, 0, 0); + + const timeUntilChange = changeTime.getTime() - currentTime.getTime(); + const minutesUntil = Math.floor(timeUntilChange / (1000 * 60)); + const hoursUntil = Math.floor(minutesUntil / 60); + const remainingMinutes = minutesUntil % 60; + + return { + ...entry, + changeTime, + timeUntilChange, + minutesUntil, + hoursUntil, + remainingMinutes + }; + }); +} + +/** + * Get the current active mode based on schedule (mirrors scheduler logic) + * @param {Date} currentTime - Current time to calculate from + * @returns {string} Current mode name + */ +export function getCurrentScheduledMode(currentTime = new Date()) { + const adjustedSchedule = getAdjustedSchedule(currentTime); + const currentHour = currentTime.getHours(); + const currentMinute = currentTime.getMinutes(); + + let mode = 'Off'; // Default mode + + // Find the most recent schedule entry that has already occurred + for (let entry of adjustedSchedule) { + const { adjustedHour, adjustedMinute } = entry; + + // Check if current time is past the adjusted schedule time + if (adjustedHour < currentHour || (adjustedHour === currentHour && adjustedMinute <= currentMinute)) { + mode = entry.mode; + } + } + + return mode; +} + diff --git a/src/app/api/schedule/route.js b/src/app/api/schedule/route.js new file mode 100644 index 0000000..684436e --- /dev/null +++ b/src/app/api/schedule/route.js @@ -0,0 +1,26 @@ +import { NextResponse } from 'next/server'; +import { getNextScheduledChange, getUpcomingChanges, getCurrentScheduledMode } from '../schedule-utils'; +import { handleSuccess, handleServerError } from '../utils'; + +export async function GET() { + try { + const currentTime = new Date(); + const nextChange = getNextScheduledChange(currentTime); + const upcomingChanges = getUpcomingChanges(currentTime); + const currentScheduledMode = getCurrentScheduledMode(currentTime); + + const data = { + currentTime: currentTime.toISOString(), + currentScheduledMode, + nextChange, + upcomingChanges, + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone + }; + + return handleSuccess(data); + } catch (error) { + console.error('Error in schedule API:', error); + return handleServerError(error); + } +} + diff --git a/src/app/api/state/route.js b/src/app/api/state/route.js index fd3a93c..d1c2d74 100644 --- a/src/app/api/state/route.js +++ b/src/app/api/state/route.js @@ -1,15 +1,32 @@ import { NextResponse } from 'next/server'; import * as scheduler from '../scheduler'; import { modes } from '../../../../config'; +import { getNextScheduledChange, getUpcomingChanges } from '../schedule-utils'; import { handleSuccess, handleServerError } from '../utils'; export async function GET() { try { let mode = scheduler.getMode(); let override = scheduler.getOverride(); - const data = { mode, modes, override, state: modes[mode] }; + + // Add schedule information + const currentTime = new Date(); + const nextChange = getNextScheduledChange(currentTime); + const upcomingChanges = getUpcomingChanges(currentTime); + + const data = { + mode, + modes, + override, + state: modes[mode], + schedule: { + nextChange, + upcomingChanges, + currentTime: currentTime.toISOString() + } + }; return handleSuccess(data); } catch (error) { return handleServerError(error); } -} \ No newline at end of file +} diff --git a/src/app/page.js b/src/app/page.js index 939a45c..284c468 100644 --- a/src/app/page.js +++ b/src/app/page.js @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import ModeSwitch from '../components/ModeSwitch'; +import { formatTimeUntil, formatTime12Hour, getShortTimeUntil } from '../utils/time-formatting'; // Icons for different equipment types const EquipmentIcon = ({ type, active }) => { @@ -179,17 +180,87 @@ export default function Home() {

Next Scheduled Change

- {/* This would need actual schedule data to display accurately */} -
- Coming soon... - Feature in development -
+ {controlState?.schedule?.nextChange ? ( +
+
+ + {controlState.schedule.nextChange.mode} + + + {formatTime12Hour(new Date(controlState.schedule.nextChange.changeTime))} + +
+
+ {formatTimeUntil( + controlState.schedule.nextChange.minutesUntil, + controlState.schedule.nextChange.hoursUntil, + controlState.schedule.nextChange.remainingMinutes + )} +
+ {!controlState.schedule.nextChange.isToday && ( + + Tomorrow + + )} +
+ ) : ( +
+ No more changes scheduled today +
+ )}
+ {/* Upcoming Actions Section */} + {controlState?.schedule?.upcomingChanges && controlState.schedule.upcomingChanges.length > 0 && ( +
+
+
+ + + +

Today's Upcoming Actions

+
+
+
+
+ {controlState.schedule.upcomingChanges.map((change, index) => ( +
+
+
+
+
{change.mode}
+
+ {formatTime12Hour(new Date(change.changeTime))} +
+
+
+
+
+ {getShortTimeUntil(change.minutesUntil)} +
+
+ {change.minutesUntil < 60 ? 'soon' : 'later'} +
+
+
+ ))} +
+ {controlState.schedule.upcomingChanges.length === 0 && ( +
+ No more scheduled changes today +
+ )} +
+
+ )} +
diff --git a/src/app/utils/time-formatting.js b/src/app/utils/time-formatting.js new file mode 100644 index 0000000..686f3fc --- /dev/null +++ b/src/app/utils/time-formatting.js @@ -0,0 +1,75 @@ +/** + * Format a time difference into a human-readable string + * @param {number} minutesUntil - Minutes until the event + * @param {number} hoursUntil - Hours until the event + * @param {number} remainingMinutes - Remaining minutes after hours + * @returns {string} Formatted time string + */ +export function formatTimeUntil(minutesUntil, hoursUntil, remainingMinutes) { + if (minutesUntil < 1) { + return 'in less than 1 minute'; + } else if (minutesUntil < 60) { + return `in ${minutesUntil} minute${minutesUntil === 1 ? '' : 's'}`; + } else if (hoursUntil === 1 && remainingMinutes === 0) { + return 'in 1 hour'; + } else if (remainingMinutes === 0) { + return `in ${hoursUntil} hours`; + } else if (hoursUntil === 1) { + return `in 1 hour ${remainingMinutes} minute${remainingMinutes === 1 ? '' : 's'}`; + } else { + return `in ${hoursUntil} hours ${remainingMinutes} minute${remainingMinutes === 1 ? '' : 's'}`; + } +} + +/** + * Format a Date object into a 12-hour time string + * @param {Date} date - Date to format + * @returns {string} Formatted time string (e.g., "4:30 PM") + */ +export function formatTime12Hour(date) { + return date.toLocaleTimeString('en-US', { + hour: 'numeric', + minute: '2-digit', + hour12: true + }); +} + +/** + * Format a time for display with both absolute time and relative time + * @param {Object} scheduleEntry - Schedule entry with time information + * @param {boolean} isToday - Whether the change is today or tomorrow + * @returns {string} Formatted time string + */ +export function formatScheduleTime(scheduleEntry, isToday = true) { + const { changeTime, minutesUntil, hoursUntil, remainingMinutes } = scheduleEntry; + const absoluteTime = formatTime12Hour(changeTime); + const relativeTime = formatTimeUntil(minutesUntil, hoursUntil, remainingMinutes); + + if (isToday) { + return `${absoluteTime} (${relativeTime})`; + } else { + return `tomorrow at ${absoluteTime} (${relativeTime})`; + } +} + +/** + * Get a short relative time description + * @param {number} minutesUntil - Minutes until the event + * @returns {string} Short time description + */ +export function getShortTimeUntil(minutesUntil) { + if (minutesUntil < 1) { + return '< 1m'; + } else if (minutesUntil < 60) { + return `${minutesUntil}m`; + } else { + const hours = Math.floor(minutesUntil / 60); + const minutes = minutesUntil % 60; + if (minutes === 0) { + return `${hours}h`; + } else { + return `${hours}h ${minutes}m`; + } + } +} + diff --git a/yarn.lock b/yarn.lock index 7b71652..e8efc38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30,7 +30,7 @@ dependencies: tslib "^2.4.0" -"@emnapi/wasi-threads@^1.0.2", "@emnapi/wasi-threads@1.0.3": +"@emnapi/wasi-threads@1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz" integrity sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw== @@ -138,86 +138,16 @@ resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== -"@img/sharp-darwin-arm64@0.34.2": - version "0.34.2" - resolved "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz" - integrity sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg== - optionalDependencies: - "@img/sharp-libvips-darwin-arm64" "1.1.0" - -"@img/sharp-darwin-x64@0.34.2": - version "0.34.2" - resolved "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz" - integrity sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g== - optionalDependencies: - "@img/sharp-libvips-darwin-x64" "1.1.0" - -"@img/sharp-libvips-darwin-arm64@1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz" - integrity sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA== - -"@img/sharp-libvips-darwin-x64@1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz" - integrity sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ== - -"@img/sharp-libvips-linux-arm@1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz" - integrity sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA== - -"@img/sharp-libvips-linux-arm64@1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz" - integrity sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew== - -"@img/sharp-libvips-linux-ppc64@1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz" - integrity sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ== - -"@img/sharp-libvips-linux-s390x@1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz" - integrity sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA== - "@img/sharp-libvips-linux-x64@1.1.0": version "1.1.0" resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz" integrity sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q== -"@img/sharp-libvips-linuxmusl-arm64@1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz" - integrity sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w== - "@img/sharp-libvips-linuxmusl-x64@1.1.0": version "1.1.0" resolved "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz" integrity sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A== -"@img/sharp-linux-arm@0.34.2": - version "0.34.2" - resolved "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz" - integrity sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ== - optionalDependencies: - "@img/sharp-libvips-linux-arm" "1.1.0" - -"@img/sharp-linux-arm64@0.34.2": - version "0.34.2" - resolved "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz" - integrity sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q== - optionalDependencies: - "@img/sharp-libvips-linux-arm64" "1.1.0" - -"@img/sharp-linux-s390x@0.34.2": - version "0.34.2" - resolved "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz" - integrity sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw== - optionalDependencies: - "@img/sharp-libvips-linux-s390x" "1.1.0" - "@img/sharp-linux-x64@0.34.2": version "0.34.2" resolved "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz" @@ -225,13 +155,6 @@ optionalDependencies: "@img/sharp-libvips-linux-x64" "1.1.0" -"@img/sharp-linuxmusl-arm64@0.34.2": - version "0.34.2" - resolved "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz" - integrity sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA== - optionalDependencies: - "@img/sharp-libvips-linuxmusl-arm64" "1.1.0" - "@img/sharp-linuxmusl-x64@0.34.2": version "0.34.2" resolved "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz" @@ -239,28 +162,6 @@ optionalDependencies: "@img/sharp-libvips-linuxmusl-x64" "1.1.0" -"@img/sharp-wasm32@0.34.2": - version "0.34.2" - resolved "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz" - integrity sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ== - dependencies: - "@emnapi/runtime" "^1.4.3" - -"@img/sharp-win32-arm64@0.34.2": - version "0.34.2" - resolved "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz" - integrity sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ== - -"@img/sharp-win32-ia32@0.34.2": - version "0.34.2" - resolved "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz" - integrity sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw== - -"@img/sharp-win32-x64@0.34.2": - version "0.34.2" - resolved "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz" - integrity sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw== - "@isaacs/fs-minipass@^4.0.0": version "4.0.1" resolved "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz" @@ -294,15 +195,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@napi-rs/wasm-runtime@^0.2.11": - version "0.2.11" - resolved "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz" - integrity sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA== - dependencies: - "@emnapi/core" "^1.4.3" - "@emnapi/runtime" "^1.4.3" - "@tybys/wasm-util" "^0.9.0" - "@next/env@15.3.2": version "15.3.2" resolved "https://registry.npmjs.org/@next/env/-/env-15.3.2.tgz" @@ -315,26 +207,6 @@ dependencies: fast-glob "3.3.1" -"@next/swc-darwin-arm64@15.3.2": - version "15.3.2" - resolved "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.2.tgz" - integrity sha512-2DR6kY/OGcokbnCsjHpNeQblqCZ85/1j6njYSkzRdpLn5At7OkSdmk7WyAmB9G0k25+VgqVZ/u356OSoQZ3z0g== - -"@next/swc-darwin-x64@15.3.2": - version "15.3.2" - resolved "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.2.tgz" - integrity sha512-ro/fdqaZWL6k1S/5CLv1I0DaZfDVJkWNaUU3un8Lg6m0YENWlDulmIWzV96Iou2wEYyEsZq51mwV8+XQXqMp3w== - -"@next/swc-linux-arm64-gnu@15.3.2": - version "15.3.2" - resolved "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.2.tgz" - integrity sha512-covwwtZYhlbRWK2HlYX9835qXum4xYZ3E2Mra1mdQ+0ICGoMiw1+nVAn4d9Bo7R3JqSmK1grMq/va+0cdh7bJA== - -"@next/swc-linux-arm64-musl@15.3.2": - version "15.3.2" - resolved "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.2.tgz" - integrity sha512-KQkMEillvlW5Qk5mtGA/3Yz0/tzpNlSw6/3/ttsV1lNtMuOHcGii3zVeXZyi4EJmmLDKYcTcByV2wVsOhDt/zg== - "@next/swc-linux-x64-gnu@15.3.2": version "15.3.2" resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.2.tgz" @@ -345,16 +217,6 @@ resolved "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.2.tgz" integrity sha512-+uxFlPuCNx/T9PdMClOqeE8USKzj8tVz37KflT3Kdbx/LOlZBRI2yxuIcmx1mPNK8DwSOMNCr4ureSet7eyC0w== -"@next/swc-win32-arm64-msvc@15.3.2": - version "15.3.2" - resolved "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.2.tgz" - integrity sha512-LLTKmaI5cfD8dVzh5Vt7+OMo+AIOClEdIU/TSKbXXT2iScUTSxOGoBhfuv+FU8R9MLmrkIL1e2fBMkEEjYAtPQ== - -"@next/swc-win32-x64-msvc@15.3.2": - version "15.3.2" - resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.2.tgz" - integrity sha512-aW5B8wOPioJ4mBdMDXkt5f3j8pUr9W8AnlX0Df35uRWNT1Y6RIybxjnSUe+PhM+M1bwgyY8PHLmXZC6zT1o5tA== - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" @@ -512,41 +374,6 @@ source-map-js "^1.2.1" tailwindcss "4.1.11" -"@tailwindcss/oxide-android-arm64@4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz" - integrity sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg== - -"@tailwindcss/oxide-darwin-arm64@4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz" - integrity sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ== - -"@tailwindcss/oxide-darwin-x64@4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz" - integrity sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw== - -"@tailwindcss/oxide-freebsd-x64@4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz" - integrity sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA== - -"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz" - integrity sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg== - -"@tailwindcss/oxide-linux-arm64-gnu@4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz" - integrity sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ== - -"@tailwindcss/oxide-linux-arm64-musl@4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz" - integrity sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ== - "@tailwindcss/oxide-linux-x64-gnu@4.1.11": version "4.1.11" resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz" @@ -557,28 +384,6 @@ resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz" integrity sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q== -"@tailwindcss/oxide-wasm32-wasi@4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz" - integrity sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g== - dependencies: - "@emnapi/core" "^1.4.3" - "@emnapi/runtime" "^1.4.3" - "@emnapi/wasi-threads" "^1.0.2" - "@napi-rs/wasm-runtime" "^0.2.11" - "@tybys/wasm-util" "^0.9.0" - tslib "^2.8.0" - -"@tailwindcss/oxide-win32-arm64-msvc@4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz" - integrity sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w== - -"@tailwindcss/oxide-win32-x64-msvc@4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz" - integrity sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg== - "@tailwindcss/oxide@4.1.11": version "4.1.11" resolved "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz" @@ -730,71 +535,6 @@ "@typescript-eslint/types" "8.36.0" eslint-visitor-keys "^4.2.1" -"@unrs/resolver-binding-android-arm-eabi@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz" - integrity sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw== - -"@unrs/resolver-binding-android-arm64@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz" - integrity sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g== - -"@unrs/resolver-binding-darwin-arm64@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz" - integrity sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g== - -"@unrs/resolver-binding-darwin-x64@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz" - integrity sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ== - -"@unrs/resolver-binding-freebsd-x64@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz" - integrity sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw== - -"@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz" - integrity sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw== - -"@unrs/resolver-binding-linux-arm-musleabihf@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz" - integrity sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw== - -"@unrs/resolver-binding-linux-arm64-gnu@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz" - integrity sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ== - -"@unrs/resolver-binding-linux-arm64-musl@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz" - integrity sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w== - -"@unrs/resolver-binding-linux-ppc64-gnu@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz" - integrity sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA== - -"@unrs/resolver-binding-linux-riscv64-gnu@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz" - integrity sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ== - -"@unrs/resolver-binding-linux-riscv64-musl@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz" - integrity sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew== - -"@unrs/resolver-binding-linux-s390x-gnu@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz" - integrity sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg== - "@unrs/resolver-binding-linux-x64-gnu@1.11.1": version "1.11.1" resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz" @@ -805,28 +545,6 @@ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz" integrity sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA== -"@unrs/resolver-binding-wasm32-wasi@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz" - integrity sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ== - dependencies: - "@napi-rs/wasm-runtime" "^0.2.11" - -"@unrs/resolver-binding-win32-arm64-msvc@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz" - integrity sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw== - -"@unrs/resolver-binding-win32-ia32-msvc@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz" - integrity sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ== - -"@unrs/resolver-binding-win32-x64-msvc@1.11.1": - version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz" - integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g== - acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" @@ -2145,36 +1863,6 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -lightningcss-darwin-arm64@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz" - integrity sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ== - -lightningcss-darwin-x64@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz" - integrity sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA== - -lightningcss-freebsd-x64@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz" - integrity sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig== - -lightningcss-linux-arm-gnueabihf@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz" - integrity sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q== - -lightningcss-linux-arm64-gnu@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz" - integrity sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw== - -lightningcss-linux-arm64-musl@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz" - integrity sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ== - lightningcss-linux-x64-gnu@1.30.1: version "1.30.1" resolved "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz" @@ -2185,16 +1873,6 @@ lightningcss-linux-x64-musl@1.30.1: resolved "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz" integrity sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ== -lightningcss-win32-arm64-msvc@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz" - integrity sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA== - -lightningcss-win32-x64-msvc@1.30.1: - version "1.30.1" - resolved "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz" - integrity sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg== - lightningcss@1.30.1: version "1.30.1" resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz"