diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..2c06e5ae --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,10 @@ +# Default owner for everything in this repo. +* @VibhavSetlur + +# Infrastructure / release surface — keep an explicit owner. +/.github/ @VibhavSetlur +/.github/workflows/ @VibhavSetlur + +# Feedback / issue-tracking UI added in this change. +/components/feedback/ @VibhavSetlur +/app/api/feedback/ @VibhavSetlur diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..dd9cb8ab --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,116 @@ +name: Bug report +description: Something is broken or behaving unexpectedly in the ModelSEED web app. +title: "[Bug]: " +labels: ["bug", "triage"] +assignees: [] +body: + - type: markdown + attributes: + value: | + Thanks for reporting a bug! Please describe what went wrong in your own words — + no technical knowledge required. Fields marked with an asterisk are required. + + - type: dropdown + id: environment + attributes: + label: Which site were you using? + options: + - "staging.modelseed.org (staging)" + - "modelseed.org (production)" + - "localhost / dev" + - "Not sure" + validations: + required: true + + - type: dropdown + id: area + attributes: + label: Which part of the app? + options: + - FBA + - Gapfill + - Model viewer + - Biochem browser + - Media + - Genomes + - My Jobs / My Models + - Auth / Sign-in + - Other + validations: + required: true + + - type: dropdown + id: auth_status + attributes: + label: Were you signed in? + options: + - "Signed in (PATRIC / BV-BRC)" + - "Signed in (RAST)" + - "Not signed in" + - "Not sure" + validations: + required: true + + - type: textarea + id: what_happened + attributes: + label: What happened? + description: Describe the problem you saw, in your own words. + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What did you expect to happen instead? + validations: + required: false + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: "List what you did, e.g. 1) opened FBA, 2) selected model X, 3) clicked Run." + validations: + required: false + + - type: input + id: model_ref + attributes: + label: Model / object reference (if relevant) + description: "e.g. a model id, workspace path, or reaction/compound id." + validations: + required: false + + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: Drag and drop or paste any screenshots here. + validations: + required: false + + - type: input + id: browser + attributes: + label: Browser & OS + description: "e.g. Chrome 130 on macOS, Firefox on Windows." + validations: + required: false + + - type: textarea + id: console + attributes: + label: Console errors (optional) + description: "If comfortable, paste any red errors from the browser dev-tools console." + render: shell + validations: + required: false + + - type: checkboxes + id: checks + attributes: + label: Before you submit + options: + - label: I searched existing issues and did not find a duplicate. + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..c43ace2c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: ModelSEED documentation & help + url: https://modelseed.org/ + about: Browse ModelSEED docs and the app's built-in help before filing an issue. + - name: Ask on the ModelSEED mailing list + url: https://modelseed.org/ + about: For broad usage or research questions, the mailing list may be a better fit. + - name: Security disclosure (do not open a public issue) + url: https://github.com/ModelSEED/ModelSEED-UI/security/advisories/new + about: Report a security vulnerability privately via GitHub Security Advisories. diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 00000000..43324bf3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,55 @@ +name: Documentation improvement +description: Report missing, unclear, or incorrect documentation or in-app help text. +title: "[Docs]: " +labels: ["documentation", "triage"] +assignees: [] +body: + - type: markdown + attributes: + value: | + Help us improve the docs and in-app guidance. Point us at what was confusing. + + - type: dropdown + id: doc_location + attributes: + label: Where is the documentation problem? + options: + - In-app help / tooltip text + - About / version pages + - README or repository docs + - API documentation + - Team / contact info + - Other + validations: + required: true + + - type: input + id: page_url + attributes: + label: Page or URL (if applicable) + description: "e.g. https://modelseed.org/about or a repo file path." + validations: + required: false + + - type: textarea + id: problem + attributes: + label: What is missing, unclear, or wrong? + validations: + required: true + + - type: textarea + id: suggestion + attributes: + label: Suggested improvement + description: How would you word or structure it instead? + validations: + required: false + + - type: checkboxes + id: checks + attributes: + label: Before you submit + options: + - label: I searched existing issues and did not find a duplicate. + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..bd7924f3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,77 @@ +name: Feature request +description: Suggest a new capability or an improvement to the ModelSEED web app. +title: "[Feature]: " +labels: ["enhancement", "triage"] +assignees: [] +body: + - type: markdown + attributes: + value: | + Thanks for the idea! Tell us what you'd like to be able to do and why it would help. + + - type: dropdown + id: area + attributes: + label: Which part of the app is this about? + options: + - FBA + - Gapfill + - Model viewer + - Biochem browser + - Media + - Genomes + - My Jobs / My Models + - Auth / Sign-in + - New / general idea + - Other + validations: + required: true + + - type: textarea + id: request + attributes: + label: What would you like to be able to do? + validations: + required: true + + - type: textarea + id: use_case + attributes: + label: What's your use case? + description: Describe the workflow or research task this would support. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives or workarounds you've tried + validations: + required: false + + - type: dropdown + id: priority + attributes: + label: How important is this to you? + options: + - Nice to have + - Would help a lot + - Blocking my work + validations: + required: true + + - type: textarea + id: examples + attributes: + label: Examples, sketches, or screenshots + description: Anything that shows what you have in mind. You can drag images in here. + validations: + required: false + + - type: checkboxes + id: checks + attributes: + label: Before you submit + options: + - label: I searched existing issues and did not find a duplicate request. + required: false diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 00000000..4b497440 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,56 @@ +name: Question +description: Ask a question about using the ModelSEED web app or interpreting results. +title: "[Question]: " +labels: ["question", "triage"] +assignees: [] +body: + - type: markdown + attributes: + value: | + Ask anything about the app, the data, or how something works. If it turns out to be + a bug or feature idea, we'll route it from here. No technical knowledge needed. + + - type: dropdown + id: environment + attributes: + label: Which site are you using? + options: + - "staging.modelseed.org (staging)" + - "modelseed.org (production)" + - "localhost / dev" + - "Not sure" + validations: + required: false + + - type: dropdown + id: area + attributes: + label: Which part of the app is your question about? + options: + - FBA + - Gapfill + - Model viewer + - Biochem browser + - Media + - Genomes + - My Jobs / My Models + - Auth / Sign-in + - Not sure / general + validations: + required: false + + - type: textarea + id: question + attributes: + label: What would you like to know? + description: "Ask in your own words." + validations: + required: true + + - type: textarea + id: context + attributes: + label: Anything else that would help us answer? + description: "You can drag in a screenshot of what you're looking at." + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..a2b28219 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,41 @@ +## Summary + + + +## Type of change + +- [ ] Bug fix (non-breaking change that fixes an issue) +- [ ] New feature (non-breaking change that adds functionality) +- [ ] Breaking change (fix or feature that changes existing behavior) +- [ ] Documentation / chore / CI + +## Affected area(s) + + + +## Release flow awareness + +This repository ships **staging → production**: + +- [ ] This PR targets `ModelSEED/ModelSEED-UI:staging` (not `master`). +- [ ] I understand staging is validated on **staging.modelseed.org** before it is merged + to `master` and deployed to **modelseed.org** by a maintainer. +- [ ] This PR does **not** push directly to `upstream` `master`. + +## Verification (must mirror CI — `.github/workflows/ci.yml`) + +Run in the project conda env and check each: + +- [ ] `npm run lint` +- [ ] `npx tsc --noEmit` +- [ ] `npm run test:run` +- [ ] `npm run build` +- [ ] `npm audit --omit=dev --audit-level=high` + +## Screenshots / recordings (UI changes) + + + +## Notes for reviewer + + diff --git a/.gitignore b/.gitignore index 82189a45..7080eaeb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Local +,claude/ /legacy/ PRODUCTION_POLISH_PLAN.md scripts/local @@ -72,3 +73,6 @@ CLAUDE.local.md .opencode/ .gsd/.opencode/ .build-date +.claude/ +.omo/ +GEMINI.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 73bc1969..10382326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [3.1.0] - 2026-08-04 + +### Added +- User feedback FAB and dialog with `/api/feedback` proxy route and unit test +- GitHub issue templates, pull request template, and CODEOWNERS + +### Fixed +- Production `npm audit` findings resolved: Next.js upgraded to **16.3.0**, + `eslint-config-next` upgraded to **16.3.0**, and `postcss` override bumped + to **8.5.25** to close high-severity advisories + +--- + ## [3.0.1] - 2026-05-12 ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..0a14d8c0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,49 @@ +# Contributing to ModelSEED-UI + +This is the single durable, in-repository reference for how changes flow +through the project: branching and release workflow, and the versioning / +changelog policy. Keep this file authoritative — do not duplicate this +policy elsewhere. + +## Release workflow + +Remotes: `origin` is the contributor's fork, `upstream` is +`ModelSEED/ModelSEED-UI`. Contributors write code and open/update pull +requests; **Seaver** reviews, merges, and deploys. Contributors do not have +deploy access and must never push directly to `upstream`. + +For each change: + +1. **Branch off `staging`**: `git switch staging && git switch -c fix/` + (small/obvious changes may be committed straight onto local `staging`). +2. **Implement and verify** the change locally (lint, type-check, unit + tests, build — see `docs/TESTING.md`), using Conventional Commit messages. +3. **Merge to local `staging`**: `git switch staging && git merge --no-ff fix/`. +4. **Push to the fork's `staging`**: `git push origin staging`. +5. **Open a pull request to upstream `staging`**: base + `ModelSEED/ModelSEED-UI:staging`, head `:staging`. Then wait — + Seaver reviews, merges, and deploys to **staging.modelseed.org**. +6. After validation on staging, Seaver merges upstream `staging` → `master` + and deploys to **modelseed.org**. Contributors do not drive this step. + +Confirm with the repository owner before any outward-facing step (pushing, +or opening/updating a pull request). + +## Versioning, changelog & release classification + +- **Classify every change** using Conventional Commits (`feat`, `fix`, + `chore`, `docs`, etc.). +- **SemVer bump rule**: any commit set containing a `feat` → bump the minor + version (`x.Y.0`); `fix`-only changes → bump the patch version (`x.y.Z`); + a breaking change → bump the major version (`X.0.0`). +- **Every release bump must update, together, in the same change**: + 1. `VERSION.md` — the single version string (deploy source of truth, + exported by `deploy_container.sh` as `NEXT_PUBLIC_GIT_VERSION` and + synced into `package.json` by `scripts/sync-version-from-env.mjs`). + 2. `package.json`'s `"version"` field — must exactly match `VERSION.md`. + 3. `CHANGELOG.md` — add a new dated `## [x.y.z] - YYYY-MM-DD` section + immediately above the previous release entry, with `### Added` / + `### Changed` / `### Fixed` bullets describing user-visible impact. + Keep `## [Unreleased]` at the top for in-progress/known issues. +- Ship the bump through the release workflow above (branch off `staging`, + verify, merge to local `staging`, push, then PR to upstream `staging`). diff --git a/VERSION.md b/VERSION.md index 4a36342f..fd2a0186 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -3.0.0 +3.1.0 diff --git a/app/about/README.md b/app/about/README.md index 9d199c1a..94762ec7 100644 --- a/app/about/README.md +++ b/app/about/README.md @@ -51,7 +51,7 @@ **Server Function**: `getChangelog()` - reads `CHANGELOG.md` from project root **Features**: -- Version badge (v3.0.0) +- Version badge (reads the current build version via `NEXT_PUBLIC_GIT_VERSION`, sourced from `VERSION.md`) - Rendered Markdown changelog - StatusTable component showing service connectivity diff --git a/app/api/feedback/route.ts b/app/api/feedback/route.ts new file mode 100644 index 00000000..8216f338 --- /dev/null +++ b/app/api/feedback/route.ts @@ -0,0 +1,92 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { MODELSEED_REST_URL } from '@/lib/api/config'; + +interface FeedbackRequestBody { + type?: unknown; + title?: unknown; + description?: unknown; + area?: unknown; + environment?: unknown; + email?: unknown; + username?: unknown; + pageUrl?: unknown; +} + +const str = (v: unknown) => (typeof v === 'string' ? v.trim() : ''); + +export async function POST(request: NextRequest) { + let body: FeedbackRequestBody; + try { + body = (await request.json()) as FeedbackRequestBody; + } catch { + return NextResponse.json({ message: 'Invalid JSON body' }, { status: 400 }); + } + + const type = str(body.type); + const title = str(body.title); + const description = str(body.description); + const area = str(body.area); + const environment = str(body.environment); + const email = str(body.email); + const username = str(body.username); + const pageUrl = str(body.pageUrl); + + if (!title || !description) { + return NextResponse.json( + { message: 'Title and description are required.' }, + { status: 400 }, + ); + } + + const payload = { + type: type || 'Feedback', + title, + description, + area: area || 'Other', + environment: environment || 'Not sure', + pageUrl: pageUrl || undefined, + user: { + ...(username ? { username } : {}), + ...(email ? { email } : {}), + }, + }; + + const formData = new URLSearchParams({ feedback: JSON.stringify(payload) }); + + // Auth is optional for feedback; forward if the client sent it. + const upstreamAuth = request.headers.get('x-modelseed-auth'); + + try { + const response = await fetch(`${MODELSEED_REST_URL}/feedback`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded', + ...(upstreamAuth ? { Authorization: upstreamAuth } : {}), + }, + body: formData.toString(), + }); + + const rawText = await response.text(); + let parsed: unknown = null; + if (rawText) { + try { parsed = JSON.parse(rawText); } catch { parsed = { message: rawText }; } + } + + const pick = (o: unknown, k: 'msg' | 'message'): string | null => + typeof o === 'object' && o !== null && typeof (o as Record)[k] === 'string' + ? ((o as Record)[k]) + : null; + + if (!response.ok) { + const detail = pick(parsed, 'msg') ?? pick(parsed, 'message') ?? rawText ?? `HTTP ${response.status}`; + return NextResponse.json({ message: detail }, { status: response.status }); + } + + const msg = pick(parsed, 'msg') ?? pick(parsed, 'message') ?? 'Feedback submitted. Thank you!'; + return NextResponse.json({ msg }); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unable to reach feedback service.'; + return NextResponse.json({ message }, { status: 502 }); + } +} diff --git a/app/layout.tsx b/app/layout.tsx index 25487af4..72f5cb30 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -7,6 +7,7 @@ import HeaderLayoutRouter from '@/app/HeaderLayoutRouter'; import OutageBanner from '@/components/ui/OutageBanner'; import Providers from '@/components/Providers'; import { AuthProvider } from '@/components/auth/AuthProvider'; +import FeedbackFAB from '@/components/feedback/FeedbackFAB'; import type { Metadata } from "next"; import "./globals.css"; import "@/styles/icomoon/style.css"; @@ -47,6 +48,7 @@ export default function RootLayout({ > {children} + diff --git a/components/feedback/FeedbackDialog.tsx b/components/feedback/FeedbackDialog.tsx new file mode 100644 index 00000000..cc55b7b5 --- /dev/null +++ b/components/feedback/FeedbackDialog.tsx @@ -0,0 +1,257 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import TextField from '@mui/material/TextField'; +import MenuItem from '@mui/material/MenuItem'; +import Stack from '@mui/material/Stack'; +import Button from '@mui/material/Button'; +import IconButton from '@mui/material/IconButton'; +import Alert from '@mui/material/Alert'; +import CircularProgress from '@mui/material/CircularProgress'; +import Typography from '@mui/material/Typography'; +import CloseIcon from '@mui/icons-material/Close'; +import GitHubIcon from '@mui/icons-material/GitHub'; +import { useAuth } from '@/components/auth/AuthProvider'; +import { + FEEDBACK_TYPES, + AFFECTED_AREAS, + ENVIRONMENTS, + buildGitHubIssueUrl, + type FeedbackType, + type AffectedArea, + type Environment, +} from './feedbackOptions'; + +export interface FeedbackDialogProps { + open: boolean; + onClose: () => void; + /** Optional initial type (FAB opens to 'Bug' by default; Footer opens to 'Bug' too). */ + initialType?: FeedbackType; + /** Optional prefilled affected area (e.g. inferred from current route). */ + initialArea?: AffectedArea; +} + +export default function FeedbackDialog({ + open, + onClose, + initialType, + initialArea, +}: FeedbackDialogProps) { + const { user } = useAuth(); + + const [type, setType] = useState(initialType ?? 'Bug'); + const [title, setTitle] = useState(''); + const [description, setDescription] = useState(''); + const [area, setArea] = useState(initialArea ?? 'Other'); + const [environment, setEnvironment] = useState('Not sure'); + const [email, setEmail] = useState(''); + const [submitting, setSubmitting] = useState(false); + const [result, setResult] = useState<{ severity: 'success' | 'error'; msg: string } | null>(null); + const [submitted, setSubmitted] = useState(false); + + // Derive environment from window.location after mount (SSR-safe) + useEffect(() => { + const hostname = window.location.hostname; + if (hostname.includes('staging')) { + setEnvironment('staging.modelseed.org (staging)'); + } else if (hostname === 'modelseed.org') { + setEnvironment('modelseed.org (production)'); + } else if (hostname === 'localhost' || hostname.startsWith('127.')) { + setEnvironment('localhost / dev'); + } else { + setEnvironment('Not sure'); + } + }, []); + + // Reset form state when dialog closes + useEffect(() => { + if (!open) { + setType(initialType ?? 'Bug'); + setTitle(''); + setDescription(''); + setArea(initialArea ?? 'Other'); + setEmail(''); + setSubmitting(false); + setResult(null); + setSubmitted(false); + } + }, [open, initialType, initialArea]); + + const emailValid = email === '' || /^\S+@\S+\.\S+$/.test(email); + + const handleOpenGitHub = () => { + if (!title.trim() || !description.trim()) { + setSubmitted(true); + return; + } + window.open( + buildGitHubIssueUrl({ type, title: title.trim(), description: description.trim(), area, environment }), + '_blank', + 'noopener,noreferrer', + ); + }; + + const handleSubmit = async () => { + setSubmitted(true); + if (!title.trim() || !description.trim() || !emailValid) return; + + setSubmitting(true); + try { + const res = await fetch('/api/feedback', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + type, + title: title.trim(), + description: description.trim(), + area, + environment, + email: email.trim() || undefined, + username: user ?? undefined, + pageUrl: window.location.href, + }), + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error((data as { message?: string })?.message || `Request failed (${res.status})`); + setResult({ severity: 'success', msg: (data as { msg?: string })?.msg || 'Thanks! Your feedback was sent.' }); + setTitle(''); + setDescription(''); + } catch (e) { + setResult({ severity: 'error', msg: e instanceof Error ? e.message : 'Failed to send feedback.' }); + } finally { + setSubmitting(false); + } + }; + + return ( + + + Send feedback + + + + + + + + setType(e.target.value as FeedbackType)} + required + fullWidth + > + {FEEDBACK_TYPES.map((t) => ( + + {t} + + ))} + + + setTitle(e.target.value)} + required + fullWidth + error={submitted && !title.trim()} + helperText={submitted && !title.trim() ? 'Title is required.' : undefined} + /> + + setDescription(e.target.value)} + required + fullWidth + multiline + minRows={4} + error={submitted && !description.trim()} + helperText={submitted && !description.trim() ? 'Description is required.' : undefined} + /> + + setArea(e.target.value as AffectedArea)} + required + fullWidth + > + {AFFECTED_AREAS.map((a) => ( + + {a} + + ))} + + + setEnvironment(e.target.value as Environment)} + required + fullWidth + > + {ENVIRONMENTS.map((env) => ( + + {env} + + ))} + + + setEmail(e.target.value)} + fullWidth + error={!emailValid} + helperText={ + !emailValid + ? 'Enter a valid email address.' + : 'So we can follow up. Leave blank to stay anonymous.' + } + /> + + {result && ( + + {result.msg} + + )} + + + + + + + + + + ); +} diff --git a/components/feedback/FeedbackFAB.tsx b/components/feedback/FeedbackFAB.tsx new file mode 100644 index 00000000..a298f2e8 --- /dev/null +++ b/components/feedback/FeedbackFAB.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { useState } from 'react'; +import Fab from '@mui/material/Fab'; +import Tooltip from '@mui/material/Tooltip'; +import FeedbackOutlinedIcon from '@mui/icons-material/FeedbackOutlined'; +import { useAuth } from '@/components/auth/AuthProvider'; +import FeedbackDialog from './FeedbackDialog'; + +export default function FeedbackFAB(): React.ReactElement | null { + const { isAuthenticated } = useAuth(); + const [open, setOpen] = useState(false); + + if (!isAuthenticated) return null; + + return ( + <> + + setOpen(true)} + sx={{ position: 'fixed', bottom: 24, right: 24, zIndex: (t) => t.zIndex.speedDial }} + > + + + + setOpen(false)} /> + + ); +} diff --git a/components/feedback/FooterFeedbackButton.tsx b/components/feedback/FooterFeedbackButton.tsx new file mode 100644 index 00000000..5806947a --- /dev/null +++ b/components/feedback/FooterFeedbackButton.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { useState } from 'react'; +import Button from '@mui/material/Button'; +import FeedbackOutlinedIcon from '@mui/icons-material/FeedbackOutlined'; +import FeedbackDialog from './FeedbackDialog'; + +export default function FooterFeedbackButton() { + const [open, setOpen] = useState(false); + return ( + <> + + setOpen(false)} /> + + ); +} diff --git a/components/feedback/__tests__/feedback.test.tsx b/components/feedback/__tests__/feedback.test.tsx new file mode 100644 index 00000000..ade0cb9b --- /dev/null +++ b/components/feedback/__tests__/feedback.test.tsx @@ -0,0 +1,37 @@ +import { describe, it, expect } from 'vitest'; +import { buildGitHubIssueUrl } from '../feedbackOptions'; + +describe('buildGitHubIssueUrl', () => { + it('routes a Bug to bug_report.yml with bug labels and prefilled fields', () => { + const url = buildGitHubIssueUrl({ + type: 'Bug', title: 'FBA crashes', description: 'clicked run and it froze', + area: 'FBA', environment: 'staging.modelseed.org (staging)', + }); + expect(url).toContain('https://github.com/ModelSEED/ModelSEED-UI/issues/new?'); + expect(url).toContain('template=bug_report.yml'); + expect(decodeURIComponent(url)).toContain('labels=bug,triage'); + expect(decodeURIComponent(url)).toContain('title=FBA crashes'); + expect(decodeURIComponent(url)).toContain('area=FBA'); + expect(decodeURIComponent(url)).toContain('what_happened=clicked run and it froze'); + }); + + it('routes a Feature to feature_request.yml', () => { + const url = buildGitHubIssueUrl({ + type: 'Feature', title: 'Add export', description: 'export as SBML', + area: 'Model viewer', environment: 'Not sure', + }); + expect(url).toContain('template=feature_request.yml'); + expect(decodeURIComponent(url)).toContain('labels=enhancement,triage'); + expect(decodeURIComponent(url)).toContain('request=export as SBML'); + }); + + it('routes a Question to question.yml', () => { + const url = buildGitHubIssueUrl({ + type: 'Question', title: 'What is flux?', description: 'meaning of flux value', + area: 'Biochem browser', environment: 'modelseed.org (production)', + }); + expect(url).toContain('template=question.yml'); + expect(decodeURIComponent(url)).toContain('labels=question,triage'); + expect(decodeURIComponent(url)).toContain('question=meaning of flux value'); + }); +}); diff --git a/components/feedback/feedbackOptions.ts b/components/feedback/feedbackOptions.ts new file mode 100644 index 00000000..8193a7a0 --- /dev/null +++ b/components/feedback/feedbackOptions.ts @@ -0,0 +1,76 @@ +// components/feedback/feedbackOptions.ts +export const FEEDBACK_TYPES = ['Bug', 'Feature', 'Question'] as const; +export type FeedbackType = (typeof FEEDBACK_TYPES)[number]; + +export const AFFECTED_AREAS = [ + 'FBA', + 'Gapfill', + 'Model viewer', + 'Biochem browser', + 'Media', + 'Genomes', + 'My Jobs / My Models', + 'Auth / Sign-in', + 'Other', +] as const; +export type AffectedArea = (typeof AFFECTED_AREAS)[number]; + +export const ENVIRONMENTS = [ + 'staging.modelseed.org (staging)', + 'modelseed.org (production)', + 'localhost / dev', + 'Not sure', +] as const; +export type Environment = (typeof ENVIRONMENTS)[number]; + +// Maps the dialog "Type" to the GitHub Issue Form template filename + label to prefill. +export const GITHUB_REPO = 'ModelSEED/ModelSEED-UI'; +export const TEMPLATE_BY_TYPE: Record = { + Bug: 'bug_report.yml', + Feature: 'feature_request.yml', + Question: 'question.yml', +}; +export const LABELS_BY_TYPE: Record = { + Bug: ['bug', 'triage'], + Feature: ['enhancement', 'triage'], + Question: ['question', 'triage'], +}; + +// Maps a dialog Environment value to the exact GitHub form option label (they match today, +// but keep the indirection so a divergence never silently breaks prefill). +export const ENV_TO_GH_OPTION: Record = { + 'staging.modelseed.org (staging)': 'staging.modelseed.org (staging)', + 'modelseed.org (production)': 'modelseed.org (production)', + 'localhost / dev': 'localhost / dev', + 'Not sure': 'Not sure', +}; + +/** + * Build a prefilled GitHub "new issue" URL for the given feedback fields. + * Field ids used for prefill must match the Issue Form field `id`s in Phase 2. + */ +export function buildGitHubIssueUrl(input: { + type: FeedbackType; + title: string; + description: string; + area: AffectedArea; + environment: Environment; +}): string { + const template = TEMPLATE_BY_TYPE[input.type]; + const params = new URLSearchParams(); + params.set('template', template); + params.set('title', `${input.title}`); + params.set('labels', LABELS_BY_TYPE[input.type].join(',')); + // Per-field prefill (ids exist in the corresponding template): + params.set('area', input.area); + if (input.type === 'Bug') { + params.set('environment', ENV_TO_GH_OPTION[input.environment]); + params.set('what_happened', input.description); + } else if (input.type === 'Feature') { + params.set('request', input.description); + } else { + params.set('environment', ENV_TO_GH_OPTION[input.environment]); + params.set('question', input.description); + } + return `https://github.com/${GITHUB_REPO}/issues/new?${params.toString()}`; +} diff --git a/components/layout/Footer.tsx b/components/layout/Footer.tsx index 0ed208fb..ec43de70 100644 --- a/components/layout/Footer.tsx +++ b/components/layout/Footer.tsx @@ -6,6 +6,7 @@ import Typography from '@mui/material/Typography'; import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; import GitHubIcon from '@mui/icons-material/GitHub'; +import FooterFeedbackButton from '@/components/feedback/FooterFeedbackButton'; export default function Footer() { return ( @@ -99,6 +100,9 @@ export default function Footer() { > + + + diff --git a/docs/README.md b/docs/README.md index 6625aad2..1fa149c8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,6 +15,7 @@ This directory contains the technical documentation for the ModelSEED-UI applica | **Testing Platform** | [TESTING.md](./TESTING.md) | Vitest unit tests, Playwright E2E tests, and CI/CD pipeline. | | **Deploying** | [DEPLOYMENT.md](./DEPLOYMENT.md) | Environment variables, deployment modes, URL resolution, and configuration guide. | | **Contributing** | [DEVELOPER_GUIDE.md](./DEVELOPER_GUIDE.md) | Guidelines for maintaining the documentation. | +| **Release Process** | [CONTRIBUTING.md](../CONTRIBUTING.md) | Branching/staging workflow, and the versioning & changelog policy. | ## Project Design Principles diff --git a/package-lock.json b/package-lock.json index 43927759..b2e47da6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "modelseed-ui", - "version": "3.0.0", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "modelseed-ui", - "version": "3.0.0", + "version": "3.1.0", "dependencies": { "@emotion/cache": "^11.14.0", "@emotion/react": "^11.14.0", @@ -17,7 +17,7 @@ "@mui/x-data-grid": "^8.27.3", "@rdkit/rdkit": "2025.3.4-1.0.0", "@tanstack/react-query": "^5.90.21", - "next": "^16.2.6", + "next": "^16.3.0", "react": "19.2.3", "react-dom": "19.2.3", "react-markdown": "^10.1.0", @@ -34,7 +34,7 @@ "@vitest/coverage-v8": "^4.1.1", "dotenv": "^17.3.1", "eslint": "^9", - "eslint-config-next": "^16.2.6", + "eslint-config-next": "^16.3.0", "happy-dom": "^20.8.4", "tsx": "^4.21.0", "typescript": "^5", @@ -321,6 +321,7 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1133,9 +1134,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", "cpu": [ "arm64" ], @@ -1145,19 +1146,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.2" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", "cpu": [ "x64" ], @@ -1167,19 +1168,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", "cpu": [ "arm64" ], @@ -1193,9 +1213,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", "cpu": [ "x64" ], @@ -1209,15 +1229,12 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1228,15 +1245,12 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1247,15 +1261,12 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1266,15 +1277,12 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1285,15 +1293,12 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1304,9 +1309,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", "cpu": [ "x64" ], @@ -1320,15 +1325,12 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1339,15 +1341,12 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1358,134 +1357,119 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.2" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.2" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.2" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.2" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.2" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", "cpu": [ "x64" ], @@ -1495,88 +1479,108 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-wasm32/node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", "cpu": [ "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.3" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", "cpu": [ "arm64" ], @@ -1586,16 +1590,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", "cpu": [ "ia32" ], @@ -1605,16 +1609,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", "cpu": [ "x64" ], @@ -1624,7 +1628,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -2046,25 +2050,26 @@ } }, "node_modules/@next/env": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz", - "integrity": "sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.0.tgz", + "integrity": "sha512-o9r1S0BNiNreHP9Vs+Qnqd9kviDkJh8xIACY7UFZSmiGbbQRzPBBosvHzAU4TULHOIuOj/18RSsyz2qrREmIFw==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.6.tgz", - "integrity": "sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.3.0.tgz", + "integrity": "sha512-OqgJ8PN0d04KcPhDX/PTY5tJUJZxlbrt7O7FBsm4XE0XW2JDrKnDXsc9uo9WUimJGPoo2j+JRGhyXApC//mvbw==", "dev": true, "license": "MIT", "dependencies": { + "@eslint-community/eslint-utils": "4.9.1", "fast-glob": "3.3.1" } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz", - "integrity": "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.0.tgz", + "integrity": "sha512-55hpqq18bEVAlxedlTt3tFqZmKg2nUXT1kn1G/BGEy0R13h3LwtwHPVzzjG6P4LLeOHE32PFDQUVaJEWvBEZBw==", "cpu": [ "arm64" ], @@ -2078,9 +2083,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz", - "integrity": "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.0.tgz", + "integrity": "sha512-SOi96kSaF5T+0wW4koiM1bWzSPwjzTesC1p3df+FjdOi5LIQkBK/blxh7HdoKnNuI4PURF1OO7TZqtfnbWDSgw==", "cpu": [ "x64" ], @@ -2094,15 +2099,12 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz", - "integrity": "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.0.tgz", + "integrity": "sha512-P0gZAoPMF4dyTRzhmkV4PrqVzSOB6t4mC1oI3c4dqijJ+OVEVx5clIXAKR4/uQpsqw2KKM/0D5tVumcR2r5blg==", "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2113,15 +2115,12 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz", - "integrity": "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.0.tgz", + "integrity": "sha512-tXXGKJw0m37O0eKJARVTX/TheKPhz0QFVtVVZXmOig+9YKLQOSP6hvf2pxv5DO7CLEJyTHx3Pg043CDQkv1G4Q==", "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2132,15 +2131,12 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz", - "integrity": "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.0.tgz", + "integrity": "sha512-pjGxK5EY7yWml78ALejFkWmgHsU7wbFQrISiugpH6FbUJhgEvw3xFZ/EBAtLl7QtL0WdQKiG9eWJ3mOKGTukHw==", "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2151,15 +2147,12 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz", - "integrity": "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.0.tgz", + "integrity": "sha512-sjo++Xx+lomlPs3HRsHWhVDyGG6ms1kGW5EtHLERdII8AyG1i+f6aq68xHREO6AEMlhjTNEWBSmfJfqm9orf7g==", "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2170,9 +2163,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz", - "integrity": "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.0.tgz", + "integrity": "sha512-C5JSgiO54wURdaxdEUIXqkz04uMqC9UmPX1gtDrV/5Tf1UowdWYI8uA5hfFbPolTlp0q4KZ60xlHePNibf0VIw==", "cpu": [ "arm64" ], @@ -2186,9 +2179,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz", - "integrity": "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.0.tgz", + "integrity": "sha512-fDOggsweNb5SSw0ZKVk6U+gxSyGFFlIBY/LBc1r8GUj4u/6t6oArL+Pmkg0MBnsgR+KkdsURilVH4F3GXUGepA==", "cpu": [ "x64" ], @@ -2392,9 +2385,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2412,9 +2402,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2432,9 +2419,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2452,9 +2436,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2489,9 +2470,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3282,9 +3260,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3299,9 +3274,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3316,9 +3288,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3333,9 +3302,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3350,9 +3316,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3367,9 +3330,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3398,9 +3358,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4922,13 +4879,13 @@ } }, "node_modules/eslint-config-next": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.6.tgz", - "integrity": "sha512-z2ELYSkyrrJ6cuunTU8vhsT/RpouPkjaSah06nVW6Rg2Hpg0Vs8s497/e5s8G8qtdp4ccsiovz5P1rv+5VSW2Q==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.3.0.tgz", + "integrity": "sha512-lPrf1kHsMJEZqO0uXkNB400c5MGrhrTk3BNX7P0ol4gt61+iUlQfjy9TyIOEA9eOXrf+5+mYbT/JsY8+zqUByQ==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.2.6", + "@next/eslint-plugin-next": "16.3.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", @@ -5512,6 +5469,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -6803,9 +6761,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -6827,9 +6782,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -6872,9 +6824,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -7722,9 +7671,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "funding": [ { "type": "github", @@ -7763,16 +7712,16 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz", - "integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/next/-/next-16.3.0.tgz", + "integrity": "sha512-NEdGOzH+08eTXMUp9UYkA99Nhi5N6Thrhc1jgFOQgfgnGK/dA2hRwBpXep+exdFQrnwlRf/3Wixyp8lLBUpE2A==", "license": "MIT", "dependencies": { - "@next/env": "16.2.6", + "@next/env": "16.3.0", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", + "postcss": "8.5.23", "styled-jsx": "5.1.6" }, "bin": { @@ -7782,15 +7731,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.6", - "@next/swc-darwin-x64": "16.2.6", - "@next/swc-linux-arm64-gnu": "16.2.6", - "@next/swc-linux-arm64-musl": "16.2.6", - "@next/swc-linux-x64-gnu": "16.2.6", - "@next/swc-linux-x64-musl": "16.2.6", - "@next/swc-win32-arm64-msvc": "16.2.6", - "@next/swc-win32-x64-msvc": "16.2.6", - "sharp": "^0.34.5" + "@next/swc-darwin-arm64": "16.3.0", + "@next/swc-darwin-x64": "16.3.0", + "@next/swc-linux-arm64-gnu": "16.3.0", + "@next/swc-linux-arm64-musl": "16.3.0", + "@next/swc-linux-x64-gnu": "16.3.0", + "@next/swc-linux-x64-musl": "16.3.0", + "@next/swc-win32-arm64-msvc": "16.3.0", + "@next/swc-win32-x64-msvc": "16.3.0", + "sharp": "^0.35.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -8201,9 +8150,9 @@ } }, "node_modules/postcss": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", - "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "funding": [ { "type": "opencollective", @@ -8220,7 +8169,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -8737,54 +8686,59 @@ } }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/sharp/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "optional": true, "bin": { diff --git a/package.json b/package.json index 1966ca0b..9db3604f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "modelseed-ui", - "version": "3.0.0", + "version": "3.1.0", "private": true, "scripts": { "predev": "node scripts/sync-version-from-env.mjs", @@ -29,7 +29,7 @@ "@mui/x-data-grid": "^8.27.3", "@rdkit/rdkit": "2025.3.4-1.0.0", "@tanstack/react-query": "^5.90.21", - "next": "^16.2.6", + "next": "^16.3.0", "react": "19.2.3", "react-dom": "19.2.3", "react-markdown": "^10.1.0", @@ -46,7 +46,7 @@ "@vitest/coverage-v8": "^4.1.1", "dotenv": "^17.3.1", "eslint": "^9", - "eslint-config-next": "^16.2.6", + "eslint-config-next": "^16.3.0", "happy-dom": "^20.8.4", "tsx": "^4.21.0", "typescript": "^5", @@ -54,6 +54,6 @@ "vitest": "^4.1.1" }, "overrides": { - "postcss": "^8.5.12" + "postcss": "^8.5.25" } }