packages/web: fix lucide github; optimize Vite deps in dev mode

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I75110e204c8eda63096bcfed619a6f5c6a6a6964
This commit is contained in:
raf 2026-04-14 08:31:56 +03:00
commit 6ec645f3de
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
10 changed files with 1080 additions and 402 deletions

View file

@ -3,6 +3,7 @@
"target": "ES2022", "target": "ES2022",
"module": "ES2022", "module": "ES2022",
"lib": ["ES2022"], "lib": ["ES2022"],
"types": ["node"],
"moduleResolution": "bundler", "moduleResolution": "bundler",
"outDir": "./dist", "outDir": "./dist",
"rootDir": "./src", "rootDir": "./src",

View file

@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>

After

Width:  |  Height:  |  Size: 822 B

6
packages/web/src/@types/lucide.d.ts vendored Normal file
View file

@ -0,0 +1,6 @@
declare module 'lucide-solid/icons/*' {
import { LucideProps } from 'lucide-solid/dist/types/types';
import { Component } from 'solid-js';
const cmp: Component<LucideProps>;
export = cmp;
}

View file

@ -1,7 +1,10 @@
import { Component, For, createSignal, createMemo, Show } from 'solid-js'; import { Component, For, createSignal, createMemo, Show } from 'solid-js';
import { ComparisonEntry, calculateChange } from '@ns/core'; import { ComparisonEntry, calculateChange } from '@ns/core';
import { formatBytes, formatNumber, formatTime, formatPercent } from '@ns/ui-utils'; import { formatBytes, formatNumber, formatTime, formatPercent } from '@ns/ui-utils';
import { ArrowRight, ArrowDown, ArrowUp, X } from 'lucide-solid'; import ArrowRightIcon from 'lucide-solid/icons/arrow-right';
import ArrowDownIcon from 'lucide-solid/icons/arrow-down';
import ArrowUpIcon from 'lucide-solid/icons/arrow-up';
import XIcon from 'lucide-solid/icons/x';
interface ComparisonViewProps { interface ComparisonViewProps {
entries: ComparisonEntry[]; entries: ComparisonEntry[];
@ -106,7 +109,7 @@ const ComparisonView: Component<ComparisonViewProps> = props => {
</select> </select>
</div> </div>
<div class="compare-arrow"> <div class="compare-arrow">
<ArrowRight size={20} /> <ArrowRightIcon size={20} />
</div> </div>
<div class="compare-selector"> <div class="compare-selector">
<label>Current</label> <label>Current</label>
@ -127,7 +130,7 @@ const ComparisonView: Component<ComparisonViewProps> = props => {
<div class="snapshot-item"> <div class="snapshot-item">
<span class="snapshot-name">{entry.name}</span> <span class="snapshot-name">{entry.name}</span>
<button class="delete-btn" onClick={() => props.onDelete(entry.id)}> <button class="delete-btn" onClick={() => props.onDelete(entry.id)}>
<X size={16} /> <XIcon size={16} />
</button> </button>
</div> </div>
)} )}
@ -195,10 +198,10 @@ const ComparisonView: Component<ComparisonViewProps> = props => {
<Show when={row.isDifferent} fallback={<span class="neutral"></span>}> <Show when={row.isDifferent} fallback={<span class="neutral"></span>}>
<span class="change-value"> <span class="change-value">
<Show when={row.isReduction}> <Show when={row.isReduction}>
<ArrowDown size={14} /> <ArrowDownIcon size={14} />
</Show> </Show>
<Show when={!row.isReduction}> <Show when={!row.isReduction}>
<ArrowUp size={14} /> <ArrowUpIcon size={14} />
</Show> </Show>
{Math.abs(row.change).toFixed(2)}% {Math.abs(row.change).toFixed(2)}%
</span> </span>
@ -221,13 +224,13 @@ const ComparisonView: Component<ComparisonViewProps> = props => {
<div class="comparison-summary"> <div class="comparison-summary">
<Show when={comparison()?.some(r => r.isReduction)}> <Show when={comparison()?.some(r => r.isReduction)}>
<div class="summary-good"> <div class="summary-good">
<ArrowDown size={16} />{' '} <ArrowDownIcon size={16} />{' '}
{comparison()?.filter(r => r.isReduction && r.isDifferent).length} improved {comparison()?.filter(r => r.isReduction && r.isDifferent).length} improved
</div> </div>
</Show> </Show>
<Show when={comparison()?.some(r => !r.isReduction && r.isDifferent)}> <Show when={comparison()?.some(r => !r.isReduction && r.isDifferent)}>
<div class="summary-bad"> <div class="summary-bad">
<ArrowUp size={16} />{' '} <ArrowUpIcon size={16} />{' '}
{comparison()?.filter(r => !r.isReduction && r.isDifferent).length} regressed {comparison()?.filter(r => !r.isReduction && r.isDifferent).length} regressed
</div> </div>
</Show> </Show>

View file

@ -1,6 +1,7 @@
import { createSignal, Show, For } from 'solid-js'; import { createSignal, Show, For } from 'solid-js';
import { StatsData, ComparisonEntry } from '@ns/core'; import { StatsData, ComparisonEntry } from '@ns/core';
import { BarChart2, Clock } from 'lucide-solid'; import BarChart2Icon from 'lucide-solid/icons/bar-chart-2';
import ClockIcon from 'lucide-solid/icons/clock';
interface FileUploadProps { interface FileUploadProps {
onFileLoad: (data: StatsData, raw: Record<string, unknown>) => void; onFileLoad: (data: StatsData, raw: Record<string, unknown>) => void;
@ -44,7 +45,7 @@ export default function FileUpload(props: FileUploadProps) {
<div class="upload-container"> <div class="upload-container">
<div class="upload-card"> <div class="upload-card">
<div class="upload-icon"> <div class="upload-icon">
<BarChart2 size={48} /> <BarChart2Icon size={48} />
</div> </div>
<h2>Load Statistics</h2> <h2>Load Statistics</h2>
@ -97,7 +98,7 @@ export default function FileUpload(props: FileUploadProps) {
<Show when={props.snapshots && props.snapshots.length > 0}> <Show when={props.snapshots && props.snapshots.length > 0}>
<div class="recent-analyses"> <div class="recent-analyses">
<h3> <h3>
<Clock size={16} /> <ClockIcon size={16} />
Recent Analyses Recent Analyses
</h3> </h3>
<div class="snapshot-list"> <div class="snapshot-list">

View file

@ -1,5 +1,5 @@
import { Component, createSignal, type JSX, Show } from 'solid-js'; import { Component, createSignal, type JSX, Show } from 'solid-js';
import { ChevronDown } from 'lucide-solid'; import ChevronDownIcon from 'lucide-solid/icons/chevron-down';
interface SectionProps { interface SectionProps {
title: string; title: string;
@ -16,7 +16,7 @@ const Section: Component<SectionProps> = props => {
<Show when={props.collapsible}> <Show when={props.collapsible}>
<button class="section-header" onClick={() => setCollapsed(!collapsed())}> <button class="section-header" onClick={() => setCollapsed(!collapsed())}>
<span class="section-title">{props.title}</span> <span class="section-title">{props.title}</span>
<ChevronDown size={16} class="section-toggle" /> <ChevronDownIcon size={16} class="section-toggle" />
</button> </button>
</Show> </Show>
<Show when={!props.collapsible || !collapsed()}> <Show when={!props.collapsible || !collapsed()}>

View file

@ -1,6 +1,9 @@
import { createSignal, Show, For, onMount, createEffect, lazy } from 'solid-js'; import { createSignal, Show, For, onMount, createEffect, lazy } from 'solid-js';
import { render } from 'solid-js/web'; import { render } from 'solid-js/web';
import { Github, Save, Upload, Trash2, X } from 'lucide-solid'; import SaveIcon from 'lucide-solid/icons/save';
import UploadIcon from 'lucide-solid/icons/upload';
import Trash2Icon from 'lucide-solid/icons/trash-2';
import XIcon from 'lucide-solid/icons/x';
import FileUpload from './components/FileUpload'; import FileUpload from './components/FileUpload';
import { StatsData, ComparisonEntry, parseStats } from '@ns/core'; import { StatsData, ComparisonEntry, parseStats } from '@ns/core';
import './styles.css'; import './styles.css';
@ -184,7 +187,7 @@ function App() {
onClick={() => setShowManageSnapshots(true)} onClick={() => setShowManageSnapshots(true)}
title="Manage Snapshots" title="Manage Snapshots"
> >
<Trash2 size={16} /> <Trash2Icon size={16} />
</button> </button>
</Show> </Show>
</nav> </nav>
@ -255,7 +258,7 @@ function App() {
rel="noopener noreferrer" rel="noopener noreferrer"
class="footer-link" class="footer-link"
> >
<Github size={16} /> <img src="/assets/github.svg" alt="GitHub" width={16} height={16} />
Source Source
</a> </a>
</footer> </footer>
@ -266,7 +269,7 @@ function App() {
<div class="modal-header"> <div class="modal-header">
<h3>Manage Snapshots</h3> <h3>Manage Snapshots</h3>
<button class="close-btn" onClick={() => setShowManageSnapshots(false)}> <button class="close-btn" onClick={() => setShowManageSnapshots(false)}>
<X size={20} /> <XIcon size={20} />
</button> </button>
</div> </div>
<div class="snapshot-list-manage"> <div class="snapshot-list-manage">
@ -281,7 +284,7 @@ function App() {
</span> </span>
</div> </div>
<button class="delete-btn" onClick={() => deleteSnapshot(entry.id)}> <button class="delete-btn" onClick={() => deleteSnapshot(entry.id)}>
<X size={16} /> <XIcon size={16} />
</button> </button>
</div> </div>
)} )}
@ -290,7 +293,7 @@ function App() {
<Show when={snapshots().length > 0}> <Show when={snapshots().length > 0}>
<div class="modal-actions"> <div class="modal-actions">
<button class="danger-btn" onClick={clearAllSnapshots}> <button class="danger-btn" onClick={clearAllSnapshots}>
<Trash2 size={16} /> <Trash2Icon size={16} />
Clear All Clear All
</button> </button>
</div> </div>
@ -306,10 +309,10 @@ function App() {
onClick={() => setShowSaveDialog(true)} onClick={() => setShowSaveDialog(true)}
title="Save Snapshot" title="Save Snapshot"
> >
<Save size={20} /> <SaveIcon size={20} />
</button> </button>
<button class="action-btn clear" onClick={() => setCurrentStats(null)} title="Load New"> <button class="action-btn clear" onClick={() => setCurrentStats(null)} title="Load New">
<Upload size={20} /> <UploadIcon size={20} />
</button> </button>
</div> </div>
</Show> </Show>

View file

@ -168,6 +168,10 @@ body {
transition: color 0.2s; transition: color 0.2s;
} }
.footer-link img {
fill: currentColor;
}
.footer-link:hover { .footer-link:hover {
color: var(--text-primary); color: var(--text-primary);
} }

View file

@ -1,8 +1,16 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import solidPlugin from 'vite-plugin-solid'; import solidPlugin from 'vite-plugin-solid';
import { fileURLToPath } from 'node:url';
export default defineConfig({ export default defineConfig({
plugins: [solidPlugin()], plugins: [solidPlugin()],
resolve: {
alias: {
'lucide-solid/icons': fileURLToPath(
new URL('./node_modules/lucide-solid/dist/source/icons', import.meta.url),
),
},
},
base: process.env.GITHUB_PAGES ? '/nix-evaluator-stats/' : './', base: process.env.GITHUB_PAGES ? '/nix-evaluator-stats/' : './',
server: { server: {
port: 3000, port: 3000,

1415
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff