Skip to content

Overview almost complete #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 18, 2025
Merged

Overview almost complete #1

merged 12 commits into from
Apr 18, 2025

Conversation

michaelnji
Copy link
Owner

@michaelnji michaelnji commented Apr 18, 2025

Overview section 70% complete. This PR is to debug a bug with the timeMetrics component.

Summary by Sourcery

Enhance the application's configuration and add new components for time metrics, device information, and data fetching

New Features:

  • Implement time metrics component to display sunrise, sunset, and day length
  • Add device information component to show battery, FPS, and network status
  • Create a watchlist component for tracking financial instrument rates
  • Develop an anime image of the day component

Enhancements:

  • Update Nuxt configuration with new modules and plugins
  • Refactor theme configuration with expanded font and color options
  • Implement local storage management for caching API responses
  • Add geolocation-based time and weather metrics

Build:

  • Update package dependencies
  • Configure Tailwind CSS and UnoCSS
  • Add runtime configuration for API endpoints

CI:

  • Integrate new store management with Pinia
  • Add persistent state management
  • Configure client-side data fetching and caching

Summary by CodeRabbit

  • New Features

    • Introduced a dashboard with real-time device info, current time, weather, football matches, watchlist, daily quote, and anime images.
    • Added interactive tab management and responsive grid layouts for better navigation and organization.
    • Implemented customizable UI components such as badges, buttons, skeleton loaders, and progress bars.
    • Integrated light and dark themes with enhanced styling and glassmorphism effects.
  • Bug Fixes

    • Improved error handling and loading states for API-driven content.
  • Chores

    • Added and updated project dependencies for UI, state management, and theming.
    • Removed unused font files and related documentation to streamline assets.
  • Documentation

    • Updated configuration and license files for clarity and compliance.

@michaelnji michaelnji added bug Something isn't working test labels Apr 18, 2025
Copy link

sourcery-ai bot commented Apr 18, 2025

Reviewer's Guide by Sourcery

This pull request introduces a significant expansion of the application's features and styling. It includes new modules, plugins, components, API endpoints, and data management strategies. The changes cover a wide range of areas, including UI components, data fetching, state management, and theming.

Sequence diagram for fetching rates

sequenceDiagram
    participant Client
    participant RatesStore
    participant API Endpoint
    participant Currency API

    Client->>RatesStore: fetchRates(body)
    RatesStore->>API Endpoint: POST /api/finance/rates with body
    API Endpoint->>Currency API: GET currency data
    Currency API-->>API Endpoint: Returns currency data
    API Endpoint-->>RatesStore: Returns rates data
    RatesStore-->>Client: Updates rates
Loading

Sequence diagram for fetching images

sequenceDiagram
    participant Client
    participant AnimeImageStore
    participant API Endpoint
    participant Anime API

    Client->>AnimeImageStore: fetchImages()
    AnimeImageStore->>API Endpoint: GET /api/anime/images
    API Endpoint->>Anime API: GET image data
    Anime API-->>API Endpoint: Returns image data
    API Endpoint-->>AnimeImageStore: Returns images data
    AnimeImageStore-->>Client: Updates images
Loading

Sequence diagram for fetching sunset data

sequenceDiagram
    participant Client
    participant TimeMetrics Component
    participant API Endpoint
    participant Sunset API

    Client->>TimeMetrics Component: Mounts
    TimeMetrics Component->>API Endpoint: POST /api/weather/sunset with location data
    API Endpoint->>Sunset API: GET sunset data
    Sunset API-->>API Endpoint: Returns sunset data
    API Endpoint-->>TimeMetrics Component: Returns sunset data
    TimeMetrics Component-->>Client: Updates UI with sunset data
Loading

Class diagram for DayInfo

classDiagram
    class DayInfo {
        +results: Results
        +status: string
        +tzid: string
    }

    class Results {
        +sunrise: string
        +sunset: string
        +solar_noon: string
        +day_length: number
        +civil_twilight_begin: string
        +civil_twilight_end: string
        +nautical_twilight_begin: string
        +nautical_twilight_end: string
        +astronomical_twilight_begin: string
        +astronomical_twilight_end: string
    }

    DayInfo -- Results : contains
Loading

Class diagram for CurrencyBeaconResponse

classDiagram
    class CurrencyBeaconResponse {
        +meta: Meta
        +response: Response
        +date: string
        +base: string
        +rates: Rates
    }

    class Meta {
        +code: number
        +disclaimer: string
    }

    class Response {
        +date: string
        +base: string
        +rates: Rates
    }

    class Rates {
        + [quote: string]: number
    }

    CurrencyBeaconResponse -- Meta : has
    CurrencyBeaconResponse -- Response : has
    CurrencyBeaconResponse -- Rates : has
    Response -- Rates : has
Loading

Class diagram for NekoImage

classDiagram
    class NekoImage {
        +artist_href: string
        +artist_name: string
        +source_url: string
        +url: string
    }
Loading

Class diagram for PairRate

classDiagram
    class PairRate {
        +pair: string
        +rate: number
    }
Loading

Class diagram for Quote

classDiagram
    class Quote {
        +author: string
        +quote: string
    }
Loading

File-Level Changes

Change Details Files
Updated the Nuxt configuration file to include new modules, plugins, and configurations for styling and functionality.
  • Added tailwindcss as a Vite plugin.
  • Included pinia-plugin-persistedstate/nuxt, shadcn-nuxt, and nuxt-time as modules.
  • Configured piniaPluginPersistedstate for local storage persistence.
  • Added shadcn configuration with a component directory.
  • Added runtime configuration for various API URLs and keys.
  • Updated font weights to include a wider range (100-900).
  • Removed @nuxtjs/tailwindcss module.
nuxt.config.ts
Modified the main application view to include a layout and apply global styles.
  • Imported custom CSS files for general styling and theme.
  • Wrapped the NuxtPage component within a NuxtLayout component.
  • Applied background classes to the main div.
app.vue
Defined custom theme settings, including colors and fonts.
  • Added several custom fonts.
  • Defined a color palette with various shades of 'puce' and 'gray'.
  • Removed the fontFamily section.
theme.config.ts
Added new CSS rules for background gradients.
  • Defined CSS classes bg-1, bg-2, bg-3, and bg-4 with linear gradient backgrounds.
app.vue
Added utility CSS classes for creating glassmorphism effects.
  • Defined .glass and .glass-no-shadow classes using rgba background, box-shadow, and backdrop-filter for a frosted glass appearance.
assets/css/custom.css
Configured Tailwind CSS with custom theme and plugins.
  • Set the theme to use the custom userTheme defined in theme.config.ts.
tailwind.config.ts
Configured UnoCSS with custom theme, presets, and transformers.
  • Set the theme to use the custom userTheme defined in theme.config.ts.
uno.config.ts
Added Tailwind CSS base styles and custom theme definitions.
  • Added @import statements for Tailwind CSS and tw-animate-css.
  • Defined custom CSS variables for colors based on OKLCH color space.
  • Defined light and dark mode color schemes using the :root and .dark selectors.
  • Defined custom font variables.
assets/css/tailwind.css
Added a component to display a watchlist of instruments with their rates.
  • Created a component to display a watchlist of instruments with their rates.
  • Fetches and displays rates for predefined instruments.
  • Includes a refresh button to update the rates.
  • Uses Skeleton components as placeholders while loading.
components/watchlist.vue
Created an API endpoint to fetch currency exchange rates.
  • Accepts a list of base and quote currencies in the request body.
  • Fetches exchange rates from an external API.
  • Returns a list of currency pairs and their rates.
server/api/finance/rates.post.ts
Added a component to display random anime images.
  • Fetches and displays random anime images from an API.
  • Uses NuxtImg for optimized image loading.
  • Includes Skeleton components as placeholders while loading.
components/picOfDay.vue
Created an API endpoint to fetch sunset and sunrise information.
  • Accepts latitude, longitude, date, timezone, and format parameters in the request body.
  • Fetches sunset and sunrise information from an external API.
  • Returns the data with a 200 status code.
server/api/weather/sunset.post.ts
Added new types for API responses and data structures.
  • Defined types for DayInfo, CurrencyBeaconResponse, NekoImage, PairRate, Quote, StatusCode, ErrorCodes, ServerData, and ServerResponse.
server/types/index.types.ts
Added utility functions for sending server responses.
  • Created a sendServerResponse function to standardize server responses with status codes, messages, and data.
server/utils/response.ts
Added a component to display time metrics, including day length, sunrise, and sunset times.
  • Uses useGeolocation to get the user's coordinates.
  • Fetches sunset and sunrise information from an API.
  • Displays the information in a user-friendly format.
  • Includes Skeleton components as placeholders while loading.
components/timeMetrics.vue
Created a Pinia store to manage currency rates.
  • Defines a store with rates as a reactive reference.
  • Includes actions to fetch rates from an API and store them in local storage.
  • Includes actions to fetch rates from local storage.
stores/rates.store.ts
Added utility functions for managing data in local storage.
  • Created functions to get, set, or clear items in local storage.
lib/dbManager.ts
Created a Pinia store to manage anime images.
  • Defines a store with images as a reactive reference.
  • Includes actions to fetch images from an API and store them in local storage.
  • Includes actions to fetch images from local storage.
stores/anime-images.store.ts
Added a component to display a clock with the current time and date.
  • Uses useNow to get the current time.
  • Formats the time and date using date-fns.
  • Displays the formatted time and date.
components/clock.vue
Added a component to manage tabs.
  • Creates a tab manager component with predefined tabs.
  • Emits an event when the active tab changes.
components/tabManager.vue
Added a component to display device information.
  • Uses VueUse composables to get device information such as battery level, FPS, and network status.
  • Displays the device information in a grid layout.
components/deviceInfo.vue
Added UI components from reka-ui and Shadcn.
  • Added a progress bar component.
  • Added a button component.
  • Added a badge component.
  • Added a skeleton component.
components/ui/progress/Progress.vue
components/ui/button/index.ts
components/ui/button/Button.vue
components/ui/badge/Badge.vue
components/ui/badge/index.ts
components/ui/skeleton/Skeleton.vue
components/ui/skeleton/index.ts
components/ui/progress/index.ts
Updated the main page to include new components and a tab manager.
  • Added a clock component.
  • Added a tab manager component.
  • Added an overview tab component.
pages/index.vue
Created an API endpoint to fetch a quote.
  • Fetches a quote from an external API.
  • Returns the quote data.
server/api/quotes.get.ts
Added a component to display a quote.
  • Fetches a quote from an API.
  • Displays the quote.
  • Includes Skeleton components as placeholders while loading.
components/quote.vue
Added a component to display football match information.
  • Displays football match information.
components/football.vue
Added a component to display an overview tab.
  • Displays device information, time metrics, football match information, a quote, and random anime images.
components/overviewTab.vue

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!
  • Generate a plan of action for an issue: Comment @sourcery-ai plan on
    an issue to generate a plan of action for it.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

coderabbitai bot commented Apr 18, 2025

Walkthrough

This update introduces a comprehensive overhaul and expansion of the application's UI, state management, API integration, and theming infrastructure. It adds numerous new Vue components for dashboards, device and financial data, and utility UI elements, alongside new Pinia stores for managing anime images and currency rates with localStorage caching. Several new API endpoints are implemented for fetching anime images, currency rates, quotes, and sunrise/sunset data, all using standardized server response types. The configuration for Nuxt, Tailwind, and theming is refactored to support advanced theming, font management, and plugin integration. Multiple font and license files are removed, with new font configuration approaches adopted. Supporting utility libraries and CSS files are added, and package dependencies are significantly expanded to accommodate these features.

Changes

File(s) / Path(s) Change Summary
app.vue Updated imports to include new CSS files, restructured template to use layouts and new background classes, and added inline gradient styles.
assets/css/custom.css, assets/css/tailwind.css Added new CSS files for glassmorphism effects, custom themes, dark mode, and font variables.
components.json Added new ShadCN Vue configuration for components, Tailwind, and aliases.
components/clock.vue, components/deviceInfo.vue, components/football.vue, components/overviewTab.vue, components/picOfDay.vue, components/quote.vue, components/tabManager.vue, components/timeMetrics.vue, components/watchlist.vue Added new Vue 3 components for dashboard, device info, time, weather, quotes, watchlists, and tab management.
components/ui/badge/Badge.vue, components/ui/badge/index.ts, components/ui/button/Button.vue, components/ui/button/index.ts, components/ui/progress/Progress.vue, components/ui/progress/index.ts, components/ui/skeleton/Skeleton.vue, components/ui/skeleton/index.ts Introduced new UI primitives (Badge, Button, Progress, Skeleton) with variant-driven styling and type-safe props.
lib/dbManager.ts Added utility functions for localStorage CRUD operations and JSON handling.
nuxt.config.ts Updated to disable devtools, add new modules and plugins, configure persisted state, fonts, runtime config, and theming.
package.json Added dependencies for Tailwind, UI libraries, date utilities, state management, and icon packs.
pages/index.vue Added new main page component with greeting, clock, tab manager, and overview dashboard.
public/fonts/ClashDisplay_Complete/Fonts/WEB/README.md, public/fonts/ClashDisplay_Complete/Fonts/WEB/css/clash-display.css, public/fonts/ClashDisplay_Complete/License/FFL.txt, public/fonts/Poppins_Complete/Fonts/WEB/README.md, public/fonts/Poppins_Complete/Fonts/WEB/css/poppins.css, public/fonts/Poppins_Complete/License/OFL.txt, public/fonts/handbuck/stylesheet.css Removed font CSS, README, and license files for Clash Display, Poppins, and Handbuck.
public/fonts/terminator-real-nfi/N Freeware License.txt Added freeware license file for Terminator Real NFI font.
server/api/anime/images.get.ts, server/api/finance/rates.post.ts, server/api/quotes.get.ts, server/api/weather/sunset.post.ts Added new API endpoints for anime images, currency rates, quotes, and sunrise/sunset data with error handling.
server/types/index.types.ts Introduced strongly-typed server response and data models for API consistency.
server/utils/response.ts Added utility to standardize server responses for both success and error scenarios.
stores/anime-images.store.ts, stores/rates.store.ts Added Pinia stores for anime images and currency rates with caching and localStorage support.
tailwind.config.ts Refactored Tailwind config to use direct theme assignment instead of extending.
theme.config.ts Replaced fontFamily with a new font property for font mapping.
uno.config.ts Minor formatting adjustment (trailing comma removal).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI_Component
    participant Pinia_Store
    participant API_Endpoint
    participant External_API

    User->>UI_Component: Interacts (e.g., loads dashboard)
    UI_Component->>Pinia_Store: Fetch data (e.g., images, rates)
    Pinia_Store->>Pinia_Store: Check localStorage for cache
    alt Cache valid
        Pinia_Store->>UI_Component: Return cached data
    else Cache missing/expired
        Pinia_Store->>API_Endpoint: Request fresh data
        API_Endpoint->>External_API: Fetch from external service
        External_API-->>API_Endpoint: Return data
        API_Endpoint->>Pinia_Store: Return standardized response
        Pinia_Store->>UI_Component: Return new data
        Pinia_Store->>Pinia_Store: Update cache
    end
    UI_Component->>User: Display data (with loading states)
Loading

Poem

In the garden of code, new features bloom bright,
With dashboards and badges, and themes set just right.
The bunny hops swiftly through storage and state,
Fetching rates, fetching quotes—oh, isn’t this great?
With gradients and glass, and a clock keeping time,
This update’s a carrot—delicious, sublime!
🥕✨

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @michaelnji - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider using environment variables instead of hardcoding API URLs and keys in nuxt.config.ts.
  • The addition of many new dependencies in package.json might increase the bundle size - consider if they're all necessary.
Here's what I looked at during the review
  • 🟡 General issues: 3 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +14 to +15
const item = db.getItem(key);
return item !== null ? JSON.parse(item) : null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Consider adding error handling when parsing JSON from localStorage.

If the stored item is corrupted or not valid JSON, JSON.parse may throw an error. A try/catch block would make the function more robust in such scenarios.

Suggested change
const item = db.getItem(key);
return item !== null ? JSON.parse(item) : null;
const item = db.getItem(key);
if (item !== null) {
try {
return JSON.parse(item);
} catch (error) {
console.error(`Error parsing JSON for key ${key}:`, error);
return null;
}
}
return null;

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 48

🧹 Nitpick comments (48)
public/fonts/terminator-real-nfi/N Freeware License.txt (6)

1-3: Standardize license header formatting
The separator on line 2 (=) is minimal and may not clearly distinguish sections. Consider using a more descriptive header or a longer divider (e.g., ==== License Agreement ====, or a line of dashes) for improved readability and consistency.


4-6: Clarify usage scope
The phrase “for all your private purposes” on line 5 is informal. To align with common licensing terminology, consider rephrasing to something like:
This font is free for personal, non‑commercial use.


7-9: Fix currency formatting and simplify sentence
On line 8, “U$ 25” is non‑standard—use “US$ 25” (no space) or “$25”. Also, streamline the clause to:
For commercial use, please purchase this font for US$ 25.


10-12: Adjust capitalization and punctuation

  • Change “Thank You.” (line 10) to “Thank you.” for consistent sentence casing.
  • The lone equals sign on line 12 duplicates the header divider; ensure it matches your chosen style or remove if unnecessary.

13-15: Capitalize label and enforce secure URL

  • Capitalize “website:” (line 13) to “Website:”.
  • Verify that “http://www.norfok.com” (line 14) is correct and consider switching to HTTPS if supported (e.g., https://www.norfok.com).

16-17: Remove extraneous trailing blank lines
Trim extra empty lines at the end of the file—ensure there’s exactly one newline at EOF to adhere to file formatting best practices.

assets/css/custom.css (1)

9-15: Good variant without shadow.

This alternate class provides a nice option when the shadow effect isn't desired. Consider combining these classes with a shared base class to reduce duplication.

+.glass-base {
+    /* From https://css.glass */
+    background: rgba(255, 255, 255, 0.37);
+    backdrop-filter: blur(7.1px);
+    -webkit-backdrop-filter: blur(7.1px);
+}
+
 .glass {
     /* From https://css.glass */
-    background: rgba(255, 255, 255, 0.37);
+    @extend .glass-base;
     box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
-    backdrop-filter: blur(7.1px);
-    -webkit-backdrop-filter: blur(7.1px);
 }

 .glass-no-shadow {
     /* From https://css.glass */
-    background: rgba(255, 255, 255, 0.37);
-
-    backdrop-filter: blur(7.1px);
-    -webkit-backdrop-filter: blur(7.1px);
+    @extend .glass-base;
 }
server/utils/response.ts (2)

31-33: Remove unnecessary blank lines.

These blank lines don't serve any purpose and can be removed to improve code readability.

 }


-
-
-
 /**

55-65: Simplify error response construction.

The error response object construction can be simplified for better readability.

     const res = {
         status,
         message,
-        ok: false
-
+        ok: false
     }
 
     return res
+    // Or even simpler:
+    // return { status, message, ok: false };
 
 }
lib/dbManager.ts (2)

28-35: Simplify getItemValue function.

The function has a redundant check for null. It checks db.getItem(key) !== null twice (line 30 and implicitly in line 32).

 export function getItemValue(key: string) {
 	const db = localStorage;
-	if (db.getItem(key) !== null) {
-		const item = db.getItem(key);
-		return item !== null ? JSON.parse(item) : null;
-	}
-	return null;
+	const item = db.getItem(key);
+	return item !== null ? JSON.parse(item) : null;
 }

44-50: Fix indentation and add type validation in setItemValue function.

The indentation at line 49 is inconsistent. Also, the function doesn't validate that the key is a string, only that it's not null.

 export function setItemValue(key: string, value: unknown = "")  {
-	if (key !== null) {
+	if (key && typeof key === "string") {
 		localStorage.setItem(key, JSON.stringify(value));
 		return value;
 	} 
-     return null;
+	return null;
 }
components/football.vue (1)

22-24: Add responsive styling considerations

The current background gradient is fixed. Consider adding responsive styling variations or adjusting the gradient for different screen sizes.

.bg-foot{
    background:linear-gradient(180deg, #121317, #000000);
+   @media (max-width: 768px) {
+     background:linear-gradient(160deg, #121317, #000000);
+   }
}
components/overviewTab.vue (2)

1-3: Add reactive state for component management

The script section is currently empty. Consider adding reactive state to manage the visibility or loading state of child components.

<script lang="ts" setup>
-
+import { ref } from 'vue'
+
+// Track loading states for child components
+const componentsLoading = ref({
+  timeMetrics: true,
+  watchlist: true,
+  quote: true,
+  picOfDay: true
+})
+
+// Function to update loading state
+const setComponentLoaded = (component: keyof typeof componentsLoading) => {
+  componentsLoading.value[component] = false
+}
</script>

6-17: Improve grid layout for better responsiveness

The current grid layout might not be optimal for all viewport sizes. Consider adding more breakpoints or adjusting the grid based on content priority.

-    <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
+    <div class="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3 lg:gap-4">
       <div class="space-y-4">
         <DeviceInfo />
         <TimeMetrics/>
         <Football/>
       </div>
-      <Watchlist />
+      <div class="h-full">
+        <Watchlist />
+      </div>
       <div class="space-y-4">
         <Quote />
         <PicOfDay/>
       </div>
     </div>
components/clock.vue (3)

19-19: Check the Tailwind class name

The class mt2 appears to be a typo and should likely be mt-2 to follow Tailwind CSS naming conventions.

-        <div class="flex mt2 items-center justify-between gap-x-6">
+        <div class="flex mt-2 items-center justify-between gap-x-6">

6-11: Consider adding AM/PM indicator to the time display

The time format string uses 'hh:mm' which is 12-hour format, but there's no AM/PM indicator. This could be confusing for users.

-const formatStr = 'hh:mm'
+const formatStr = 'hh:mm a' // Adds AM/PM indicator

33-52: Consider using CSS variables for text shadow values

The text shadow implementation is verbose with repetitive values. Using CSS variables would make this more maintainable and easier to adjust for different themes.

<style scoped>
+:root {
+    --shadow-distance: 3px;
+    --shadow-color: #000000;
+    --shadow-bottom: 15px;
+}
h1.clock {
    color: #fff;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
-    text-shadow: 0px -3px 0 #000000,
-        0px -3px 0 #000000,
-        0px 3px 0 #000000,
-        0px 3px 0 #000000,
-        -3px 0px 0 #000000,
-        3px 0px 0 #000000,
-        -3px 0px 0 #000000,
-        3px 0px 0 #000000,
-        -3px -3px 0 #000000,
-        3px -3px 0 #000000,
-        -3px 3px 0 #000000,
-        3px 3px 0 #000000,
-        -3px 15px 0 #000000,
-        0px 15px 0 #000000,
-        3px 15px 0 #000000;
+    text-shadow: 
+        0px calc(-1 * var(--shadow-distance)) 0 var(--shadow-color),
+        0px var(--shadow-distance) 0 var(--shadow-color),
+        calc(-1 * var(--shadow-distance)) 0px 0 var(--shadow-color),
+        var(--shadow-distance) 0px 0 var(--shadow-color),
+        calc(-1 * var(--shadow-distance)) calc(-1 * var(--shadow-distance)) 0 var(--shadow-color),
+        var(--shadow-distance) calc(-1 * var(--shadow-distance)) 0 var(--shadow-color),
+        calc(-1 * var(--shadow-distance)) var(--shadow-distance) 0 var(--shadow-color),
+        var(--shadow-distance) var(--shadow-distance) 0 var(--shadow-color),
+        calc(-1 * var(--shadow-distance)) var(--shadow-bottom) 0 var(--shadow-color),
+        0px var(--shadow-bottom) 0 var(--shadow-color),
+        var(--shadow-distance) var(--shadow-bottom) 0 var(--shadow-color);
}
</style>
server/api/anime/images.get.ts (2)

11-15: Implement caching to reduce external API calls

Consider implementing a caching strategy to reduce the number of external API calls and improve performance.

You could use an in-memory cache or leverage the Nitro cache capabilities:

+import { defineEventHandler, setResponseStatus, getHeader, setCacheHeader } from 'h3';
+
 export default eventHandler(async (event) => {
    try{
    const config = useRuntimeConfig();
 			const constructedUrl = `${config.animeUrl}/waifu?amount=3`;
 
 			const resp = await $fetch(constructedUrl);
 			const data = resp as { results: NekoImage[] };
 
+           // Set cache headers for 1 hour
+           setCacheHeader(event, {
+               maxAge: 3600,
+               staleWhileRevalidate: 86400
+           });
+
 			setResponseStatus(event, 200);
 			return sendServerResponse(200, "success", data.results);

16-25: Improve error handling with more specific error information

The current error handling could be improved by providing more specific error information for debugging.

    } catch (error) {
        setResponseStatus(event, 500);
        let message: string;
        if (error instanceof Error && error.message.includes("fetch failed")) {
            message = "fetch failed";
+           console.error("Error fetching anime images:", error.message);
        } else {
-           message = "Internal Server Error";
+           message = error instanceof Error ? error.message : "Internal Server Error";
+           console.error("Error in anime images API:", error);
        }
        return sendServerResponse(500, message);
    }
server/api/quotes.get.ts (1)

21-24: Implement caching to improve performance

Consider implementing a caching strategy to reduce external API calls and improve performance.

+import { defineEventHandler, setResponseStatus, setCacheHeader } from 'h3';

 export default eventHandler(async (event) => {
	try {
		const config = useRuntimeConfig();
		// ...fetchQuote implementation...

		const quoteData = await fetchQuote();

+		// Set cache headers for 6 hours
+		setCacheHeader(event, {
+			maxAge: 21600,
+			staleWhileRevalidate: 86400
+		});
+
		setResponseStatus(event, 200);
		return sendServerResponse(200, "success", quoteData);
app.vue (1)

21-23: Document the purpose of background classes

Add comments to explain the intended use case for each background gradient to help other developers.

+/* Light pastel green gradient - used for the main background */
.bg-2 {
  background: linear-gradient(135deg, #F8FF8Fff, #F3FF96ff, #EFFF9Dff, #EAFFA4ff, #E5FFAAff, #E0FFB1ff, #DCFFB8ff, #D7FFBFff, #D2FFC6ff, #CDFFCDff, #C9FFD3ff, #C4FFDAff, #BFFFE1ff);
}
components/tabManager.vue (1)

26-29: Fix function parameter formatting for consistency.

The function definition has an unusual line break between the parameter name and closing parenthesis. This affects code readability and consistency.

-function changeTabs(tab: string
-) {
+function changeTabs(tab: string) {
    emit("tab-change", tab)
}
server/api/weather/sunset.post.ts (3)

53-53: Fix inconsistent indentation.

There appears to be inconsistent indentation with tab characters on this line.

-				const constructedUrl = `${config.sunsetUrl}?lat=${body.lat}&lng=${body.lng}&formatted=${body.formatted}&date=${body.date}&tzid=${body.tzid}`;
+    const constructedUrl = `${config.sunsetUrl}?lat=${body.lat}&lng=${body.lng}&formatted=${body.formatted}&date=${body.date}&tzid=${body.tzid}`;

44-51: Consider adding value range validation for coordinates.

While type validation is good, consider also validating value ranges (latitude: -90 to 90, longitude: -180 to 180) to catch invalid geographic coordinates early.

   if (typeof body.lat !== 'number' ||
       typeof body.lng !== 'number' ||
       typeof body.date !== 'string' ||
       typeof body.tzid !== 'string' ||
-      (body.formatted !== 0 && body.formatted !== 1)) {
+      (body.formatted !== 0 && body.formatted !== 1) ||
+      body.lat < -90 || body.lat > 90 ||
+      body.lng < -180 || body.lng > 180) {
       setResponseStatus(event, 400)
       return sendServerResponse(400, 'Invalid payload, fields have incorrect types')
   }

55-56: Consider adding a timeout for the external API call.

The external API call lacks a timeout setting. Consider adding one to prevent hanging requests if the external service is slow or unresponsive.

-    const resp = await $fetch(constructedUrl)
+    const resp = await $fetch(constructedUrl, { timeout: 10000 }) // 10 second timeout
     const data = resp as DayInfo
components/deviceInfo.vue (4)

1-7: Consolidate multiple imports from the same package.

Since you're importing multiple functions from '@vueuse/core', consider consolidating them into a single import statement for better readability.

-import { useMemory } from '@vueuse/core'
-import { useBattery } from '@vueuse/core'
-import { useFps } from '@vueuse/core'
-import { useNetwork } from '@vueuse/core'
+import { useMemory, useBattery, useFps, useNetwork } from '@vueuse/core'

7-7: Remove unused variables from destructuring assignment.

Several variables from the useNetwork() call are not used in the component. Remove them to improve code clarity and performance.

-const { isOnline, offlineAt, downlink, downlinkMax, effectiveType, saveData, type } = useNetwork()
+const { effectiveType } = useNetwork()

10-13: Remove unused function.

The size() function is defined but never used in the component. Remove it to keep the code clean.

-function size(v: number) {
-    const kb = v / 1024 / 1024
-    return `${kb.toFixed(2)} MB`
-}

15-15: Remove unused battery variables.

Only charging and level from the battery API are used in the template. Remove the unused variables.

-const { charging, chargingTime, dischargingTime, level } = useBattery()
+const { charging, level } = useBattery()
components/quote.vue (2)

23-32: Consider adding fallback content when loading fails

The template shows a loading state but doesn't handle the case where loading fails or no quote is available. Consider showing a fallback message instead of an empty component.

Add a condition to display a fallback message:

<div>
    <div class="rounded-3xl h12rem bg-white p6">
        <h3 class="uppercase opacity-80 text-lg mb2">Quote of the day</h3>
        <Skeleton v-if="isLoading" class="w-full h-2rem bg-stone-200 rounded-xl" />
        <Skeleton v-if="isLoading" class="w-full h-2rem mt2 bg-stone-200 rounded-xl" />
        <Skeleton v-if="isLoading" class="w-full h-2rem mt2 bg-stone-200 rounded-xl" />
        <h2 class="text-3xl font-bold" v-if="!isLoading">
            {{ quote }}
        </h2>
+       <h2 class="text-3xl font-bold" v-if="!isLoading && !quote">
+           No quote available today. Check back later!
+       </h2>
    </div>
</div>

23-24: Standardize CSS class naming

The component uses unconventional CSS class naming without hyphens between property and value (e.g., "h12rem", "p6" instead of "h-12rem", "p-6").

Consider using standard Tailwind CSS class naming conventions with hyphens:

-<div class="rounded-3xl h12rem bg-white p6">
+<div class="rounded-3xl h-12rem bg-white p-6">
components/picOfDay.vue (1)

39-42: Address commented-out code

The component contains commented-out code for displaying artist names. Either implement this feature or remove the commented code to keep the codebase clean.

Either remove the commented code or uncomment and implement the feature:

-<!-- <div v-if="!isLoading && animeImageStore.images" class="mt1">
-    <h2 class="text-base font-extrabold">{{ animeImageStore.images[0].artist_name }}</h2>
-</div> -->
components/ui/progress/Progress.vue (1)

35-39: Consider simplifying the null check in the transform style.

The null coalescing operator on line 38 is redundant since you've already defaulted modelValue to 0 in the props definition.

-      :style="`transform: translateX(-${100 - (props.modelValue ?? 0)}%);`"
+      :style="`transform: translateX(-${100 - props.modelValue}%);`"
assets/css/tailwind.css (1)

1-6: Good setup of Tailwind CSS with custom variant.

The imports and custom variant for dark mode are well-defined. However, there's a commented-out import on line 5 that should either be removed or uncommented if needed.

@import "tailwindcss";
@import "tw-animate-css";

@custom-variant dark (&:is(.dark *));
-/* @import "tailwindcss-primeui"; */
pages/index.vue (2)

1-11: Good implementation of time-based greeting logic.

The greeting logic effectively uses date-fns and the useNow composable to display time-appropriate greetings. However, consider extracting the hardcoded name "Michael" to a configuration variable or user profile for better flexibility.

-    if (currentHour < 13 && currentHour >= 0) return 'Good morning, Michael'
-    if (currentHour < 17 && currentHour >= 13) return 'Good day, Michael'
-    if (currentHour < 19 && currentHour >= 17) return 'Good evening, Michael'
-    return 'Good night, Michael'
+    const userName = useUserStore().name || 'Michael' // Or from any appropriate source
+    if (currentHour < 13 && currentHour >= 0) return `Good morning, ${userName}`
+    if (currentHour < 17 && currentHour >= 13) return `Good day, ${userName}`
+    if (currentHour < 19 && currentHour >= 17) return `Good evening, ${userName}`
+    return `Good night, ${userName}`

34-36: Remove empty style tags.

The style tags are empty and can be removed to keep the code clean.

-<style>
-
-</style>
components/ui/button/index.ts (1)

5-7: Very long base class string could be more readable.

The base class string in the cva function is quite long, making it harder to read and maintain. Consider breaking it down into multiple concatenated strings for better readability.

-export const buttonVariants = cva(
-  'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
+export const buttonVariants = cva(
+  [
+    'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium',
+    'transition-all disabled:pointer-events-none disabled:opacity-50',
+    '[&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4 shrink-0 [&_svg]:shrink-0',
+    'outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
+    'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive'
+  ].join(' '),
stores/anime-images.store.ts (2)

31-49: Fix indentation inconsistencies and add error handling

There are indentation inconsistencies in the fetchImagesFromStorage function, and it also lacks error handling similar to fetchImages.

Fix the indentation and add error handling:

const fetchImagesFromStorage = async () => {
+   isLoading.value = true;
+   error.value = null;
+   try {
        const imagesFromStorage = getItemValue('neko-img') as {
            data: NekoImage[];
            timestamp: Date;
            expires: Date;
        } | null;
        if (imagesFromStorage) {
            if (isFuture(imagesFromStorage.expires)) {
                images.value = imagesFromStorage.data;
            } else {
                // If the images are expired, fetch new images
-               await fetchImages();
+               await fetchImages();
            }
        } else {
            // If there are no images in storage, fetch new images
-          await  fetchImages();
+           await fetchImages();
        }
+   } catch (e) {
+       error.value = e instanceof Error ? e.message : 'Failed to fetch images from storage';
+       console.error('Error fetching images from storage:', e);
+   } finally {
+       isLoading.value = false;
+   }
};

23-27: Replace useNow() with standard Date constructor

Using useNow().value is more verbose than needed and potentially less efficient than using the standard Date constructor.

setItemValue("neko-img", {
    data: response.data,
-   timestamp: useNow().value,
-   expires: addHours(useNow().value, 6),
+   timestamp: new Date(),
+   expires: addHours(new Date(), 6),
});
server/api/finance/rates.post.ts (1)

78-89: Use specific error types for better error handling

The error handling is good, but could be improved by using specific error types or codes to help the client better understand the nature of the failure.

Enhance error type reporting:

catch (error) {
    setResponseStatus(event, 500);
    let message: string;
+   let errorCode = 'UNKNOWN_ERROR';
    if (error instanceof Error) {
        message = error.message;
+       // Add specific error identification
+       if (message.includes('fetch failed')) {
+           errorCode = 'NETWORK_ERROR';
+           message = 'fetch failed';
+       } else if (message.includes('rate limit')) {
+           errorCode = 'RATE_LIMIT_EXCEEDED';
+       } else if (message.includes('invalid api key')) {
+           errorCode = 'INVALID_API_KEY';
+       }
    } else {
        message = String(error);
    }
    return sendServerResponse(
        500,
-       message.includes("fetch failed") ? "fetch failed" : message,
+       message,
+       null,
+       { errorCode }
    );
}
stores/rates.store.ts (1)

25-29: Use standard Date constructor instead of useNow()

Using useNow().value is more verbose than necessary and potentially less efficient.

setItemValue("rates", {
	data: response.data,
-	timestamp: useNow().value,
-	expires: addHours(useNow().value, 1),
+	timestamp: new Date(),
+	expires: addHours(new Date(), 1),
});
components/timeMetrics.vue (2)

25-25: Remove debug console.log statements

This console.log should be removed before production deployment.

-        console.log(coords.value)

64-65: Remove unnecessary empty lines

There are multiple empty lines at the end of the file that should be removed.

-
-
 
components/watchlist.vue (2)

2-2: Remove unused imports

The component imports types that aren't directly used in the component.

-import type { PairRate, StatusCode, ServerResponse } from '~/server/types/index.types'
+import type { PairRate } from '~/server/types/index.types'

97-105: Consider implementing or removing commented code

The commented-out code suggests a plan to show rate changes with directional indicators, but this functionality isn't implemented yet. Consider either implementing this feature or removing the commented code.

-                    <!-- <div class="flex items-end gap-x-1">
-                        <Icon name="solar:alt-arrow-down-bold"  class="text-red-400" v-if="item.change < 0" size="26" />
-                        <Icon name="solar:alt-arrow-up-bold"  class="text-green-400" v-if="item.change > 0" size="26" />
-                        <p class="text-lg mt2 font-bold"
-                            :class="{ '!text-green-500': item.change > 0, '!text-red-500': item.change < 0 }">{{
-                            item.change
-                            }}%</p>
-
-                    </div> -->
server/types/index.types.ts (3)

35-42: Remove unnecessary empty lines in NekoImage type

There are unnecessary empty lines in the NekoImage type definition.

 export type NekoImage = {
-
     artist_href: string,
     artist_name: string,
     source_url: string,
     url: string
-
 };

54-55: Simplify error codes definition

The ErrorCodes type is redundant as it's just StatusCode minus 200. It could be defined more concisely.

 export type StatusCode = 200 | 400 | 404 | 403 | 500 | 401;
-export type ErrorCodes = 400 | 404 | 403 | 500 | 401;
+export type ErrorCodes = Exclude<StatusCode, 200>;
 export type ServerData = null | DayInfo | NekoImage[] | PairRate[] | Quote;

56-65: Add JSDoc documentation to ServerResponse interface

The ServerResponse interface uses conditional types which can be difficult to understand without documentation.

+/**
+ * Generic server response interface that handles both successful and error responses
+ * @template Status - The HTTP status code
+ * @template Data - The payload data type
+ */
 export interface ServerResponse<
     Status extends StatusCode,
     Data extends ServerData,
 > {
     status: Status;
     message?: string;
     ok: Status extends 200 ? true : false;
     data?: Status extends 200 ? Data : null;
     error?: Status extends 200 ? null : string;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2bd2e1 and d4442f4.

⛔ Files ignored due to path filters (210)
  • bun.lock is excluded by !**/*.lock
  • public/fonts/Acro Mono DCU/Acro Mono DCU/- Desktop Commercial Use License - Pixel Surplus.pdf is excluded by !**/*.pdf
  • public/fonts/Acro Mono DCU/Acro Mono DCU/AcroMono.otf is excluded by !**/*.otf
  • public/fonts/Acro Mono DCU/Acro Mono DCU/AcroMono.ttf is excluded by !**/*.ttf
  • public/fonts/Acro Mono DCU/__MACOSX/Acro Mono DCU/._- Desktop Commercial Use License - Pixel Surplus.pdf is excluded by !**/*.pdf
  • public/fonts/Acro Mono DCU/__MACOSX/Acro Mono DCU/._AcroMono.otf is excluded by !**/*.otf
  • public/fonts/Acro Mono DCU/__MACOSX/Acro Mono DCU/._AcroMono.ttf is excluded by !**/*.ttf
  • public/fonts/Adblox DCU/Adblox DCU/- Desktop Commercial Use License - Pixel Surplus.pdf is excluded by !**/*.pdf
  • public/fonts/Adblox DCU/Adblox DCU/Adblox.otf is excluded by !**/*.otf
  • public/fonts/Adblox DCU/__MACOSX/Adblox DCU/._- Desktop Commercial Use License - Pixel Surplus.pdf is excluded by !**/*.pdf
  • public/fonts/Adblox DCU/__MACOSX/Adblox DCU/._Adblox.otf is excluded by !**/*.otf
  • public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Bold.otf is excluded by !**/*.otf
  • public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Extralight.otf is excluded by !**/*.otf
  • public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Light.otf is excluded by !**/*.otf
  • public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Medium.otf is excluded by !**/*.otf
  • public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Regular.otf is excluded by !**/*.otf
  • public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Semibold.otf is excluded by !**/*.otf
  • public/fonts/ClashDisplay_Complete/Fonts/TTF/ClashDisplay-Variable.ttf is excluded by !**/*.ttf
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Bold.eot is excluded by !**/*.eot
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Bold.ttf is excluded by !**/*.ttf
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Bold.woff is excluded by !**/*.woff
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Bold.woff2 is excluded by !**/*.woff2
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Extralight.eot is excluded by !**/*.eot
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Extralight.ttf is excluded by !**/*.ttf
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Extralight.woff is excluded by !**/*.woff
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Extralight.woff2 is excluded by !**/*.woff2
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Light.eot is excluded by !**/*.eot
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Light.ttf is excluded by !**/*.ttf
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Light.woff is excluded by !**/*.woff
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Light.woff2 is excluded by !**/*.woff2
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Medium.eot is excluded by !**/*.eot
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Medium.ttf is excluded by !**/*.ttf
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Medium.woff is excluded by !**/*.woff
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Medium.woff2 is excluded by !**/*.woff2
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Regular.eot is excluded by !**/*.eot
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Regular.ttf is excluded by !**/*.ttf
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Regular.woff is excluded by !**/*.woff
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Regular.woff2 is excluded by !**/*.woff2
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Semibold.eot is excluded by !**/*.eot
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Semibold.ttf is excluded by !**/*.ttf
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Semibold.woff is excluded by !**/*.woff
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Semibold.woff2 is excluded by !**/*.woff2
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Variable.eot is excluded by !**/*.eot
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Variable.ttf is excluded by !**/*.ttf
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Variable.woff is excluded by !**/*.woff
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Variable.woff2 is excluded by !**/*.woff2
  • public/fonts/Khaviax.otf is excluded by !**/*.otf
  • public/fonts/LAGISTHA.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Black.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-BlackItalic.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Bold.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-BoldItalic.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-ExtraBold.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-ExtraBoldItalic.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-ExtraLight.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-ExtraLightItalic.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Italic.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Light.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-LightItalic.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Medium.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-MediumItalic.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Regular.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-SemiBold.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-SemiBoldItalic.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Thin.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/OTF/Poppins-ThinItalic.otf is excluded by !**/*.otf
  • public/fonts/Poppins_Complete/Fonts/TTF/Poppins-Variable.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/TTF/Poppins-VariableItalic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Black.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Black.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Black.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Black.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BlackItalic.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BlackItalic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BlackItalic.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BlackItalic.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Bold.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Bold.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Bold.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Bold.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BoldItalic.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BoldItalic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BoldItalic.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BoldItalic.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBold.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBold.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBold.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBold.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBoldItalic.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBoldItalic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBoldItalic.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBoldItalic.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLight.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLight.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLight.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLight.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLightItalic.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLightItalic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLightItalic.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLightItalic.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Italic.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Italic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Italic.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Italic.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Light.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Light.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Light.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Light.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-LightItalic.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-LightItalic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-LightItalic.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-LightItalic.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Medium.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Medium.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Medium.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Medium.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-MediumItalic.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-MediumItalic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-MediumItalic.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-MediumItalic.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Regular.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Regular.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Regular.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Regular.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBold.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBold.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBold.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBold.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBoldItalic.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBoldItalic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBoldItalic.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBoldItalic.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Thin.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Thin.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Thin.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Thin.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ThinItalic.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ThinItalic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ThinItalic.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ThinItalic.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Variable.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Variable.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Variable.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Variable.woff2 is excluded by !**/*.woff2
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-VariableItalic.eot is excluded by !**/*.eot
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-VariableItalic.ttf is excluded by !**/*.ttf
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-VariableItalic.woff is excluded by !**/*.woff
  • public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-VariableItalic.woff2 is excluded by !**/*.woff2
  • public/fonts/UT Defghio Nixav - Personal Use Only/- Free Personal Use License - Pixel Surplus.pdf is excluded by !**/*.pdf
  • public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-PS/UT Defghio Nixav.otf is excluded by !**/*.otf
  • public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-PS/UT Defghio Nixav.woff is excluded by !**/*.woff
  • public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-PS/UT Defghio Nixav.woff2 is excluded by !**/*.woff2
  • public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-TT/UT Defghio Nixav.ttf is excluded by !**/*.ttf
  • public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-TT/UT Defghio Nixav.woff is excluded by !**/*.woff
  • public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-TT/UT Defghio Nixav.woff2 is excluded by !**/*.woff2
  • public/fonts/action-man/Action_Man.ttf is excluded by !**/*.ttf
  • public/fonts/action-man/Action_Man_Bold.ttf is excluded by !**/*.ttf
  • public/fonts/action-man/Action_Man_Bold_Italic.ttf is excluded by !**/*.ttf
  • public/fonts/action-man/Action_Man_Extended.ttf is excluded by !**/*.ttf
  • public/fonts/action-man/Action_Man_Extended_Bold.ttf is excluded by !**/*.ttf
  • public/fonts/action-man/Action_Man_Extended_Bold_Italic.ttf is excluded by !**/*.ttf
  • public/fonts/action-man/Action_Man_Extended_Italic.ttf is excluded by !**/*.ttf
  • public/fonts/action-man/Action_Man_Italic.ttf is excluded by !**/*.ttf
  • public/fonts/action-man/Action_Man_Shaded.ttf is excluded by !**/*.ttf
  • public/fonts/action-man/Action_Man_Shaded_Italic.ttf is excluded by !**/*.ttf
  • public/fonts/espial/EspialRegular15-6Y08Y.otf is excluded by !**/*.otf
  • public/fonts/fifa-welcome/fifawelcome.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-Black.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-BlackItalic.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-Bold.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-BoldItalic.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-Italic.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-Light.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-LightItalic.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-Medium.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-MediumItalic.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-Regular.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-Thin.ttf is excluded by !**/*.ttf
  • public/fonts/google-sans/ProductSans-ThinItalic.ttf is excluded by !**/*.ttf
  • public/fonts/handbuck/Handbuck-Regular.eot is excluded by !**/*.eot
  • public/fonts/handbuck/Handbuck-Regular.ttf is excluded by !**/*.ttf
  • public/fonts/handbuck/Handbuck-Regular.woff is excluded by !**/*.woff
  • public/fonts/handbuck/Handbuck-Regular.woff2 is excluded by !**/*.woff2
  • public/fonts/intro/Intro.otf is excluded by !**/*.otf
  • public/fonts/intro/IntroDemoBlackCapsInline.otf is excluded by !**/*.otf
  • public/fonts/japanese/Japanese.ttf is excluded by !**/*.ttf
  • public/fonts/panton/FFF_EULA_license.pdf is excluded by !**/*.pdf
  • public/fonts/panton/Panton-BlackCaps.otf is excluded by !**/*.otf
  • public/fonts/panton/Panton-BlackitalicCaps.otf is excluded by !**/*.otf
  • public/fonts/panton/Panton-LightCaps.otf is excluded by !**/*.otf
  • public/fonts/panton/Panton-LightitalicCaps.otf is excluded by !**/*.otf
  • public/fonts/pixeled/Pixeled.ttf is excluded by !**/*.ttf
  • public/fonts/roboto-mono/RobotoMono-Bold.ttf is excluded by !**/*.ttf
  • public/fonts/roboto-mono/RobotoMono-BoldItalic.ttf is excluded by !**/*.ttf
  • public/fonts/roboto-mono/RobotoMono-Italic.ttf is excluded by !**/*.ttf
  • public/fonts/roboto-mono/RobotoMono-Light.ttf is excluded by !**/*.ttf
  • public/fonts/roboto-mono/RobotoMono-LightItalic.ttf is excluded by !**/*.ttf
  • public/fonts/roboto-mono/RobotoMono-Medium.ttf is excluded by !**/*.ttf
  • public/fonts/roboto-mono/RobotoMono-MediumItalic.ttf is excluded by !**/*.ttf
  • public/fonts/roboto-mono/RobotoMono-Regular.ttf is excluded by !**/*.ttf
  • public/fonts/roboto-mono/RobotoMono-Thin.ttf is excluded by !**/*.ttf
  • public/fonts/roboto-mono/RobotoMono-ThinItalic.ttf is excluded by !**/*.ttf
  • public/fonts/terminator-real-nfi/opentype/terminator real nfi.otf is excluded by !**/*.otf
  • public/fonts/terminator-real-nfi/opentype/terminator real nfi.ttf is excluded by !**/*.ttf
  • public/fonts/terminator-real-nfi/truetype/terminator real nfi.ttf is excluded by !**/*.ttf
  • public/fonts/uni-sans/FFF_EULA_license.pdf is excluded by !**/*.pdf
  • public/fonts/uni-sans/Uni Sans Heavy Italic.otf is excluded by !**/*.otf
  • public/fonts/uni-sans/Uni Sans Heavy.otf is excluded by !**/*.otf
  • public/fonts/uni-sans/Uni Sans Thin Italic.otf is excluded by !**/*.otf
  • public/fonts/uni-sans/Uni Sans Thin.otf is excluded by !**/*.otf
📒 Files selected for processing (44)
  • app.vue (1 hunks)
  • assets/css/custom.css (1 hunks)
  • assets/css/tailwind.css (1 hunks)
  • components.json (1 hunks)
  • components/clock.vue (1 hunks)
  • components/deviceInfo.vue (1 hunks)
  • components/football.vue (1 hunks)
  • components/overviewTab.vue (1 hunks)
  • components/picOfDay.vue (1 hunks)
  • components/quote.vue (1 hunks)
  • components/tabManager.vue (1 hunks)
  • components/timeMetrics.vue (1 hunks)
  • components/ui/badge/Badge.vue (1 hunks)
  • components/ui/badge/index.ts (1 hunks)
  • components/ui/button/Button.vue (1 hunks)
  • components/ui/button/index.ts (1 hunks)
  • components/ui/progress/Progress.vue (1 hunks)
  • components/ui/progress/index.ts (1 hunks)
  • components/ui/skeleton/Skeleton.vue (1 hunks)
  • components/ui/skeleton/index.ts (1 hunks)
  • components/watchlist.vue (1 hunks)
  • lib/dbManager.ts (1 hunks)
  • nuxt.config.ts (1 hunks)
  • package.json (1 hunks)
  • pages/index.vue (1 hunks)
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/README.md (0 hunks)
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/css/clash-display.css (0 hunks)
  • public/fonts/ClashDisplay_Complete/License/FFL.txt (0 hunks)
  • public/fonts/Poppins_Complete/Fonts/WEB/README.md (0 hunks)
  • public/fonts/Poppins_Complete/Fonts/WEB/css/poppins.css (0 hunks)
  • public/fonts/Poppins_Complete/License/OFL.txt (0 hunks)
  • public/fonts/handbuck/stylesheet.css (0 hunks)
  • public/fonts/terminator-real-nfi/N Freeware License.txt (1 hunks)
  • server/api/anime/images.get.ts (1 hunks)
  • server/api/finance/rates.post.ts (1 hunks)
  • server/api/quotes.get.ts (1 hunks)
  • server/api/weather/sunset.post.ts (1 hunks)
  • server/types/index.types.ts (1 hunks)
  • server/utils/response.ts (1 hunks)
  • stores/anime-images.store.ts (1 hunks)
  • stores/rates.store.ts (1 hunks)
  • tailwind.config.ts (1 hunks)
  • theme.config.ts (1 hunks)
  • uno.config.ts (1 hunks)
💤 Files with no reviewable changes (7)
  • public/fonts/handbuck/stylesheet.css
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/README.md
  • public/fonts/Poppins_Complete/Fonts/WEB/README.md
  • public/fonts/Poppins_Complete/License/OFL.txt
  • public/fonts/Poppins_Complete/Fonts/WEB/css/poppins.css
  • public/fonts/ClashDisplay_Complete/Fonts/WEB/css/clash-display.css
  • public/fonts/ClashDisplay_Complete/License/FFL.txt
🧰 Additional context used
🧬 Code Graph Analysis (9)
uno.config.ts (1)
theme.config.ts (1)
  • userTheme (2-79)
tailwind.config.ts (1)
theme.config.ts (1)
  • userTheme (2-79)
server/api/quotes.get.ts (2)
server/types/index.types.ts (1)
  • Quote (48-51)
server/utils/response.ts (1)
  • sendServerResponse (42-65)
server/api/weather/sunset.post.ts (2)
server/utils/response.ts (1)
  • sendServerResponse (42-65)
server/types/index.types.ts (1)
  • DayInfo (1-16)
server/api/anime/images.get.ts (2)
server/types/index.types.ts (1)
  • NekoImage (35-42)
server/utils/response.ts (1)
  • sendServerResponse (42-65)
server/api/finance/rates.post.ts (2)
server/utils/response.ts (1)
  • sendServerResponse (42-65)
server/types/index.types.ts (1)
  • CurrencyBeaconResponse (17-34)
stores/anime-images.store.ts (2)
server/types/index.types.ts (3)
  • NekoImage (35-42)
  • ServerResponse (56-65)
  • StatusCode (53-53)
lib/dbManager.ts (2)
  • setItemValue (44-50)
  • getItemValue (28-35)
stores/rates.store.ts (2)
server/types/index.types.ts (3)
  • PairRate (43-46)
  • ServerResponse (56-65)
  • StatusCode (53-53)
lib/dbManager.ts (2)
  • setItemValue (44-50)
  • getItemValue (28-35)
server/utils/response.ts (1)
server/types/index.types.ts (3)
  • ErrorCodes (54-54)
  • ServerData (55-55)
  • StatusCode (53-53)
🔇 Additional comments (28)
assets/css/custom.css (1)

1-7: Nice implementation of the glassmorphism effect with proper vendor prefixes.

The .glass class correctly implements the frosted glass effect with background transparency, box-shadow, and backdrop filter blur. The vendor prefix (-webkit-backdrop-filter) ensures cross-browser compatibility.

components.json (1)

1-20: Well-structured ShadCN Vue configuration.

The configuration is properly set up with appropriate schema reference, TypeScript support, Tailwind CSS settings, path aliases, and icon library configuration. This provides a solid foundation for the UI components in your application.

The setup using the "new-york" style and "stone" base color with CSS variables will create a consistent design system. The path aliases will make imports cleaner throughout the codebase.

server/utils/response.ts (3)

1-2: Good use of TypeScript imports for type safety.

The imports correctly reference the required types from the server types file.


3-14: Well-documented function overload for error responses.

The JSDoc comments for the error response overload are clear and descriptive.


16-29: Well-documented function overload for success responses.

The JSDoc comments for the success response overload are clear and descriptive.

uno.config.ts (1)

13-14: Formatting change looks good

The removal of the trailing comma and addition of a newline at the end of the file are consistent with standard code formatting practices.

components/ui/progress/index.ts (1)

1-2: Clean component export pattern

This export follows a good pattern for a component library, making the Progress component easily importable throughout the application.

components/ui/skeleton/index.ts (1)

1-2: Clean component export pattern

This export follows the same consistent pattern as other UI components, making the Skeleton component easily importable throughout the application.

components/tabManager.vue (1)

33-40: The tab UI implementation looks good!

The component correctly implements a responsive tab interface with proper active tab styling. The responsive design hides text labels on mobile and shows them on larger screens.

package.json (2)

20-39: Dependencies look appropriate for the project updates.

The added dependencies align well with the UI overhaul, state management, and theming requirements described in the PR. The package versions appear to be current and compatible.


45-47: Icon packages addition looks good.

The added iconify packages for cryptocurrency and flag icons will enhance the UI capabilities of the application.

server/api/weather/sunset.post.ts (3)

5-27: Excellent JSDoc documentation!

The comprehensive documentation for this API endpoint clearly explains its purpose, parameters, return values, and potential error cases. This is a great practice for maintainability.


38-51: Input validation is well implemented.

The code thoroughly validates both the presence of required fields and their types, returning appropriate error responses when validation fails.


62-68: Error handling is well implemented.

The error handling distinguishes between Error objects and other error types, and customizes the error message based on the error content. This is a good practice.

components/deviceInfo.vue (1)

19-38: Good implementation of device metrics display.

The device information is well organized in a responsive grid layout with appropriate icons. The component effectively leverages the browser's device APIs through Vue composables.

components/ui/badge/Badge.vue (1)

1-28: Well-structured component with good TypeScript usage

The Badge component is well-implemented with proper TypeScript type safety, clean prop handling with computed properties, and good separation of concerns. The component correctly delegates props and handles class combinations efficiently.

components/ui/badge/index.ts (1)

1-25: Well-structured badge component with good variant definitions

The badge implementation is well-structured with clean variant definitions using class-variance-authority. The component properly exports the Badge component, variant utility function, and TypeScript types for good type safety.

components/ui/progress/Progress.vue (4)

1-9: Solid component setup with proper dependencies and type definitions.

The component correctly imports necessary dependencies from reka-ui and sets up TypeScript types. The use of the utility function cn from lib/utils is a good practice for conditional class merging.


10-15: Well-structured props definition with sensible defaults.

The component properly extends ProgressRootProps while adding an optional class prop. Setting the default modelValue to 0 is appropriate for a progress component.


17-21: Clean separation of class prop from other props.

Good use of a computed property to extract the class prop from other props, preventing it from being passed directly to the ProgressRoot component.


24-34: Proper styling and prop binding for the progress root.

The component correctly binds delegated props and applies conditional class names using the cn utility.

assets/css/tailwind.css (3)

7-44: Well-organized CSS theme variables mapping.

The inline theme definition creates a clear mapping between semantic color variables and their implementation variables, which is excellent for maintainability.


46-80: Comprehensive light theme color definitions using OKLCH color space.

The root variables define a complete set of colors for the light theme using the modern OKLCH color space, which provides better perceptual uniformity across different displays.


82-115: Well-defined dark theme with appropriate color contrast.

The dark theme variables maintain proper contrast ratios while providing a cohesive dark experience. The OKLCH values appear to be carefully selected for consistency.

components/ui/button/index.ts (4)

1-4: Clean import and export setup.

The file correctly imports CVA and exports the Button component. This approach maintains separation of concerns between component implementation and styling configuration.


8-21: Comprehensive button variants with appropriate styling.

The button variants cover a wide range of use cases with appropriate styling for each. The use of semantic color variables from your theme ensures consistency across the application.


22-34: Well-defined button sizes with responsive considerations.

The size variants provide good flexibility with responsive adjustments for icons. The defaultVariants ensure consistent rendering when no specific variant is chosen.


36-36: Proper type export for type-safe usage.

Exporting the ButtonVariants type provides type safety when using the component, which is a good practice for maintainability.

Comment on lines +8 to +19
export function getOrSetItem(key: string, value: unknown) {
const db = localStorage;
if(key && typeof key === "string") {
if (db.getItem(key) === null) {
db.setItem(key, JSON.stringify(value));
} else {
const item = db.getItem(key);
return item !== null ? JSON.parse(item) : null;
}
}
return value;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix logic and indentation in getOrSetItem function.

There are issues with the logic and indentation in this function:

  1. The function returns value if the key is valid but not in storage, instead of setting it and returning it
  2. The indentation is inconsistent (line 17 is over-indented)
 export function getOrSetItem(key: string, value: unknown) {
 	const db = localStorage;
 	if(key && typeof key === "string") {
         if (db.getItem(key) === null) {
 		db.setItem(key, JSON.stringify(value));
+		return value;
 	} else {
 		const item = db.getItem(key);
 		return item !== null ? JSON.parse(item) : null;
 	}
-    }
+	}
 	return value;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function getOrSetItem(key: string, value: unknown) {
const db = localStorage;
if(key && typeof key === "string") {
if (db.getItem(key) === null) {
db.setItem(key, JSON.stringify(value));
} else {
const item = db.getItem(key);
return item !== null ? JSON.parse(item) : null;
}
}
return value;
}
export function getOrSetItem(key: string, value: unknown) {
const db = localStorage;
if (key && typeof key === "string") {
if (db.getItem(key) === null) {
db.setItem(key, JSON.stringify(value));
return value;
} else {
const item = db.getItem(key);
return item !== null ? JSON.parse(item) : null;
}
}
return value;
}

Comment on lines +3 to +16
font: {
adblox: 'Adblox',
nixav: 'UT Defghio Nixav',
'acro-mono': 'AcroMono',
panton: 'panton',
uni: 'Uni Sans Heavy',
espial: 'EspialRegular15-6Y08Y',
action: 'Action_Man',
fifa: 'fifawelcome',
intro: 'Intro',
japanese: 'Japanese',
product: "Product Sans",
sans: "Product Sans"
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Enhanced font configuration approach

The new font property provides more flexibility than the previous fontFamily property by allowing multiple named font configurations that can be used across different UI components.

A few things to consider:

  • Verify that all these fonts are properly loaded and available in your application
  • Consider adding comments for non-standard font names like 'EspialRegular15-6Y08Y'
  • Note that both 'product' and 'sans' map to the same font value - is this intentional?

🏁 Script executed:

#!/bin/bash
# Check if font files exist for the fonts defined in the theme
echo "Checking for font files in the project..."
fd -t f -e ttf -e otf -e woff -e woff2

Length of output: 2856


Fonts confirmed; minor tweaks recommended

All entries in the font map have matching files under public/fonts/…, so your font assets are properly loaded.

A couple of small improvements to consider:

  • Add an inline comment explaining non‑standard identifiers like EspialRegular15-6Y08Y for future maintainers.
  • Verify whether product and sans both mapping to "Product Sans" is an intentional alias or a typo.

Comment on lines +31 to +35
if (response.ok && response.data) {
isLoading.value = false
data.value = response.data
console.log(data)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling for API responses

The current implementation only checks if response.ok is true but doesn't handle API errors. Also, there's a console.log statement that should be removed.

            if (response.ok && response.data) {
                isLoading.value = false
                data.value = response.data
-                console.log(data)
+            } else {
+                errorMessage.value = response.error || 'Failed to fetch time metrics'
+                isLoading.value = false
            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (response.ok && response.data) {
isLoading.value = false
data.value = response.data
console.log(data)
}
if (response.ok && response.data) {
isLoading.value = false
data.value = response.data
} else {
errorMessage.value = response.error || 'Failed to fetch time metrics'
isLoading.value = false
}

Comment on lines +56 to +74
const refreshContent = async () => {
isLoading.value = true
try {
const reqBody = []
for (const item of instruments) {
reqBody.push({
pair: item.name,
base: item.name.split('/')[0],
quote: item.name.split('/')[1]
});
}
await ratesStore.fetchRates(reqBody)
isLoading.value = false;

} catch (error) {
console.log(error)

}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve refreshContent error handling and use reusable function

Use the extracted function for building the request body and improve error handling.

 const refreshContent = async () => {
     isLoading.value = true
     try {
-        const reqBody = []
-        for (const item of instruments) {
-            reqBody.push({
-                pair: item.name,
-                base: item.name.split('/')[0],
-                quote: item.name.split('/')[1]
-            });
-        }
-        await ratesStore.fetchRates(reqBody)
+        await ratesStore.fetchRates(buildRequestBody())
         isLoading.value = false;

     } catch (error) {
-        console.log(error)
+        console.error('Error refreshing rates:', error)
+        isLoading.value = false
+        // Consider showing a user-facing error message
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const refreshContent = async () => {
isLoading.value = true
try {
const reqBody = []
for (const item of instruments) {
reqBody.push({
pair: item.name,
base: item.name.split('/')[0],
quote: item.name.split('/')[1]
});
}
await ratesStore.fetchRates(reqBody)
isLoading.value = false;
} catch (error) {
console.log(error)
}
}
const refreshContent = async () => {
isLoading.value = true
try {
await ratesStore.fetchRates(buildRequestBody())
isLoading.value = false;
} catch (error) {
console.error('Error refreshing rates:', error)
isLoading.value = false
// Consider showing a user-facing error message
}
}

Comment on lines +39 to +54
try {
const reqBody = []
for (const item of instruments) {
reqBody.push({
pair: item.name,
base: item.name.split('/')[0],
quote: item.name.split('/')[1]
});
}
await ratesStore.fetchRatesFromStorage(reqBody)
isLoading.value = false;

} catch (error) {
console.log(error)

}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor duplicate code into a reusable function

The code to construct the request body is duplicated in both onMounted and refreshContent methods. Extract this into a separate function.

+const buildRequestBody = () => {
+    const reqBody = []
+    for (const item of instruments) {
+        reqBody.push({
+            pair: item.name,
+            base: item.name.split('/')[0],
+            quote: item.name.split('/')[1]
+        });
+    }
+    return reqBody
+}
+
 onMounted(async () => {
     try {
-        const reqBody = []
-        for (const item of instruments) {
-            reqBody.push({
-                pair: item.name,
-                base: item.name.split('/')[0],
-                quote: item.name.split('/')[1]
-            });
-        }
-        await ratesStore.fetchRatesFromStorage(reqBody)
+        await ratesStore.fetchRatesFromStorage(buildRequestBody())
         isLoading.value = false;

     } catch (error) {
         console.log(error)

     }
})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
const reqBody = []
for (const item of instruments) {
reqBody.push({
pair: item.name,
base: item.name.split('/')[0],
quote: item.name.split('/')[1]
});
}
await ratesStore.fetchRatesFromStorage(reqBody)
isLoading.value = false;
} catch (error) {
console.log(error)
}
const buildRequestBody = () => {
const reqBody = []
for (const item of instruments) {
reqBody.push({
pair: item.name,
base: item.name.split('/')[0],
quote: item.name.split('/')[1]
})
}
return reqBody
}
onMounted(async () => {
try {
await ratesStore.fetchRatesFromStorage(buildRequestBody())
isLoading.value = false
} catch (error) {
console.log(error)
}
})

Comment on lines +86 to +107
<div class="rounded-2xl p3 bg-stone-100" v-if="!isLoading && ratesStore.rates.length >= 1"
v-for="item, k in instruments">
<h2 class="font-extrabold mb1 text-2xl">${{
Intl.NumberFormat('us').format(ratesStore.rates[k].rate).toString() }}
</h2>
<div class="flex items-center gap-x-2">
<div class=" rounded-full grid place-items-center overflow-hidden">
<Icon :name="item.icon" size="26" />
</div>
<p class="text-base">{{ item.name }}</p>
</div>
<!-- <div class="flex items-end gap-x-1">
<Icon name="solar:alt-arrow-down-bold" class="text-red-400" v-if="item.change < 0" size="26" />
<Icon name="solar:alt-arrow-up-bold" class="text-green-400" v-if="item.change > 0" size="26" />
<p class="text-lg mt2 font-bold"
:class="{ '!text-green-500': item.change > 0, '!text-red-500': item.change < 0 }">{{
item.change
}}%</p>

</div> -->

</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error handling and rate comparison safety

The component doesn't handle cases where the API returns fewer rates than expected and uses index-based access which could be fragile.

-                <div class="rounded-2xl p3 bg-stone-100" v-if="!isLoading && ratesStore.rates.length >= 1"
+                <div class="rounded-2xl p3 bg-stone-100" v-if="!isLoading && ratesStore.rates.length >= 1"
                     v-for="item, k in instruments">
+                    <template v-if="k < ratesStore.rates.length && ratesStore.rates[k]">
                     <h2 class="font-extrabold  mb1 text-2xl">${{
                         Intl.NumberFormat('us').format(ratesStore.rates[k].rate).toString() }}
                     </h2>
                     <div class="flex items-center gap-x-2">
                         <div class=" rounded-full grid place-items-center overflow-hidden">
                             <Icon :name="item.icon" size="26" />
                         </div>
                         <p class="text-base">{{ item.name }}</p>
                     </div>
+                    </template>
+                    <div v-else class="text-red-500">
+                        Failed to load rate for {{ item.name }}
+                    </div>
                     <!-- <div class="flex items-end gap-x-1">
                         <Icon name="solar:alt-arrow-down-bold"  class="text-red-400" v-if="item.change < 0" size="26" />
                         <Icon name="solar:alt-arrow-up-bold"  class="text-green-400" v-if="item.change > 0" size="26" />
                         <p class="text-lg mt2 font-bold"
                             :class="{ '!text-green-500': item.change > 0, '!text-red-500': item.change < 0 }">{{
                             item.change
                             }}%</p>

                     </div> -->

                 </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div class="rounded-2xl p3 bg-stone-100" v-if="!isLoading && ratesStore.rates.length >= 1"
v-for="item, k in instruments">
<h2 class="font-extrabold mb1 text-2xl">${{
Intl.NumberFormat('us').format(ratesStore.rates[k].rate).toString() }}
</h2>
<div class="flex items-center gap-x-2">
<div class=" rounded-full grid place-items-center overflow-hidden">
<Icon :name="item.icon" size="26" />
</div>
<p class="text-base">{{ item.name }}</p>
</div>
<!-- <div class="flex items-end gap-x-1">
<Icon name="solar:alt-arrow-down-bold" class="text-red-400" v-if="item.change < 0" size="26" />
<Icon name="solar:alt-arrow-up-bold" class="text-green-400" v-if="item.change > 0" size="26" />
<p class="text-lg mt2 font-bold"
:class="{ '!text-green-500': item.change > 0, '!text-red-500': item.change < 0 }">{{
item.change
}}%</p>
</div> -->
</div>
<div class="rounded-2xl p3 bg-stone-100" v-if="!isLoading && ratesStore.rates.length >= 1"
v-for="item, k in instruments">
<template v-if="k < ratesStore.rates.length && ratesStore.rates[k]">
<h2 class="font-extrabold mb1 text-2xl">${{
Intl.NumberFormat('us').format(ratesStore.rates[k].rate).toString() }}
</h2>
<div class="flex items-center gap-x-2">
<div class=" rounded-full grid place-items-center overflow-hidden">
<Icon :name="item.icon" size="26" />
</div>
<p class="text-base">{{ item.name }}</p>
</div>
</template>
<div v-else class="text-red-500">
Failed to load rate for {{ item.name }}
</div>
<!-- <div class="flex items-end gap-x-1">
<Icon name="solar:alt-arrow-down-bold" class="text-red-400" v-if="item.change < 0" size="26" />
<Icon name="solar:alt-arrow-up-bold" class="text-green-400" v-if="item.change > 0" size="26" />
<p class="text-lg mt2 font-bold"
:class="{ '!text-green-500': item.change > 0, '!text-red-500': item.change < 0 }">{{
item.change
}}%</p>
</div> -->
</div>

Comment on lines +17 to +34
export type CurrencyBeaconResponse = {
meta: {
code: number;
disclaimer: string;
};
response: {
date: string;
base: string;
rates: {
[quote: string]: number;
};
};
date: string;
base: string;
rates: {
[quote: string]: number;
};
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix duplicate fields in CurrencyBeaconResponse type

There are duplicate fields (date, base, rates) at both the root level and nested under response. This could lead to confusion when using this type.

 export type CurrencyBeaconResponse = {
 	meta: {
 		code: number;
 		disclaimer: string;
 	};
 	response: {
 		date: string;
 		base: string;
 		rates: {
 			[quote: string]: number;
 		};
 	};
-	date: string;
-	base: string;
-	rates: {
-		[quote: string]: number;
-	};
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export type CurrencyBeaconResponse = {
meta: {
code: number;
disclaimer: string;
};
response: {
date: string;
base: string;
rates: {
[quote: string]: number;
};
};
date: string;
base: string;
rates: {
[quote: string]: number;
};
};
export type CurrencyBeaconResponse = {
meta: {
code: number;
disclaimer: string;
};
response: {
date: string;
base: string;
rates: {
[quote: string]: number;
};
};
};

@michaelnji michaelnji merged commit 619389d into master Apr 18, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working test
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant