diff --git a/.github/actions/package-lock.json b/.github/actions/package-lock.json index 0a48c560..8e0a8eb2 100644 --- a/.github/actions/package-lock.json +++ b/.github/actions/package-lock.json @@ -9,9 +9,102 @@ "version": "1.0.0", "license": "BSD-3 Clause", "dependencies": { + "@octokit/graphql": "^8.1.1", "yaml": "^2.8.3" } }, + "node_modules/@octokit/endpoint": { + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", + "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", + "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", + "license": "MIT" + }, + "node_modules/@octokit/request": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.4.tgz", + "integrity": "sha512-q8ybdytBmxa6KogWlNa818r0k1wlqzNC+yNkcQDECHvQo8Vmstrg18JwqJHdJdUiHD2sjlwBgSm9kHkOKe2iyA==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^10.1.4", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", + "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^25.1.0" + } + }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "license": "ISC" + }, "node_modules/yaml": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", diff --git a/.github/actions/package.json b/.github/actions/package.json index 52513de9..1fe611ef 100644 --- a/.github/actions/package.json +++ b/.github/actions/package.json @@ -10,6 +10,7 @@ "author": "allison-truhlar", "license": "BSD-3 Clause", "dependencies": { + "@octokit/graphql": "^8.1.1", "yaml": "^2.8.3" } } diff --git a/.github/actions/update-project-dates.js b/.github/actions/update-project-dates.js new file mode 100644 index 00000000..71d44577 --- /dev/null +++ b/.github/actions/update-project-dates.js @@ -0,0 +1,157 @@ +import { readFileSync, writeFileSync, readdirSync } from "fs"; +import { fileURLToPath } from "url"; +import { dirname, join } from "path"; +import yaml from "yaml"; +import { graphql } from "@octokit/graphql"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const PROJECTS_DIR = join(__dirname, "..", "..", "src", "content", "projects"); + +const authToken = process.env.GITHUB_TOKEN || process.env.OSSI_SITE_TOKEN; +if (!authToken) { + console.error( + "Missing GITHUB_TOKEN (or OSSI_SITE_TOKEN) environment variable." + ); + process.exit(1); +} + +const graphqlWithAuth = graphql.defaults({ + headers: { authorization: `token ${authToken}` }, +}); + +// extracts the frontmatter block, split into the pieces needed to splice a +// single field back in without reserializing (and reformatting) the rest of it +function extractFrontMatter(content) { + const match = content.match(/^(---\n)([\s\S]+?)(\n---)/); + return match + ? { full: match[0], prefix: match[1], body: match[2], suffix: match[3] } + : null; +} + +function parseOwnerRepo(githubUrl) { + let url; + try { + url = new URL(githubUrl); + } catch { + return null; + } + const parts = url.pathname.split("/").filter((part) => part !== ""); + if (parts.length < 2) return null; + return { owner: parts[0], repo: parts[1].replace(/\.git$/, "") }; +} + +function upsertField(frontMatterBody, key, value) { + const lineRegex = new RegExp(`^${key}:.*$`, "m"); + const newLine = `${key}: ${value}`; + if (lineRegex.test(frontMatterBody)) { + return frontMatterBody.replace(lineRegex, newLine); + } + const githubLinkRegex = /^(github link:.*)$/m; + if (githubLinkRegex.test(frontMatterBody)) { + return frontMatterBody.replace(githubLinkRegex, `$1\n${newLine}`); + } + return `${frontMatterBody}\n${newLine}`; +} + +// 1. gather project files and the owner/repo their "github link" points to +const projectFiles = readdirSync(PROJECTS_DIR).filter( + (file) => file.endsWith(".md") && !file.startsWith("_") +); + +const projectRepos = []; +for (const file of projectFiles) { + const filePath = join(PROJECTS_DIR, file); + const content = readFileSync(filePath, "utf8"); + const frontMatter = extractFrontMatter(content); + if (!frontMatter) { + console.warn(`Skipping ${file}: no frontmatter found.`); + continue; + } + + const parsed = yaml.parse(frontMatter.body); + const githubUrl = parsed["github link"]; + const ownerRepo = githubUrl ? parseOwnerRepo(githubUrl) : null; + if (!ownerRepo) { + console.warn( + `Skipping ${file}: could not parse "github link" (${githubUrl}).` + ); + continue; + } + + projectRepos.push({ file, filePath, content, frontMatter, ...ownerRepo }); +} + +// 2. batch-fetch pushedAt for every unique repo in a single GraphQL call +const repoKey = (owner, repo) => `${owner}/${repo}`.toLowerCase(); +const uniqueRepos = [ + ...new Map( + projectRepos.map((p) => [repoKey(p.owner, p.repo), p]) + ).values(), +]; + +const queryParts = []; +const variableDefs = []; +const variables = {}; +uniqueRepos.forEach((p, i) => { + queryParts.push( + `r${i}: repository(owner: $owner${i}, name: $name${i}) { nameWithOwner pushedAt }` + ); + variableDefs.push(`$owner${i}: String!`, `$name${i}: String!`); + variables[`owner${i}`] = p.owner; + variables[`name${i}`] = p.repo; +}); + +const query = `query(${variableDefs.join(", ")}) {\n${queryParts.join( + "\n" +)}\n}`; + +let result; +try { + result = await graphqlWithAuth(query, variables); +} catch (error) { + if (error.data){ + console.warn("GraphQL returned partial data:", error.message); + result = error.data; + } + else { + console.error("GraphQL query failed:", error.message); + process.exit(1); + } +} + +// 3. build repoDates in memory: repo (owner/name, lowercased) -> pushedAt +const repoDates = {}; +uniqueRepos.forEach((p, i) => { + const repoResult = result[`r${i}`]; + if (repoResult && repoResult.pushedAt) { + repoDates[repoKey(p.owner, p.repo)] = repoResult.pushedAt; + } else { + console.warn( + `No pushedAt found for ${p.owner}/${p.repo} (referenced by ${p.file})` + ); + } +}); + +// 4. splice the resulting update_date into each project's frontmatter +let updatedCount = 0; +for (const project of projectRepos) { + const pushedAt = repoDates[repoKey(project.owner, project.repo)]; + if (!pushedAt) continue; + + const updateDate = pushedAt.slice(0, 10); // YYYY-MM-DD + const newBody = upsertField( + project.frontMatter.body, + "update_date", + updateDate + ); + if (newBody === project.frontMatter.body) continue; + + const newContent = project.content.replace( + project.frontMatter.full, + `${project.frontMatter.prefix}${newBody}${project.frontMatter.suffix}` + ); + writeFileSync(project.filePath, newContent); + updatedCount++; +} + +console.log(`Updated update_date in ${updatedCount} project file(s).`); diff --git a/.github/workflows/update-project-dates.yml b/.github/workflows/update-project-dates.yml new file mode 100644 index 00000000..8a041a78 --- /dev/null +++ b/.github/workflows/update-project-dates.yml @@ -0,0 +1,57 @@ +name: Update project last-updated dates + +# Refreshes the update_date frontmatter field on every project .md file from +# each project's GitHub repository pushedAt timestamp, so the site's +# "Last updated" sort stays current without manual upkeep. +on: + schedule: + - cron: "0 6 * * 1" # every Monday at 06:00 UTC + workflow_dispatch: + +permissions: + contents: write + actions: write + +jobs: + update-dates: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "npm" + cache-dependency-path: .github/actions/package-lock.json + + - name: Install npm dependencies + run: npm ci + working-directory: .github/actions + + - name: Fetch and update project dates + run: node update-project-dates.js + working-directory: .github/actions + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Commit and push changes + id: commit + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add src/content/projects + if git diff --cached --quiet; then + echo "No changes to commit." + else + git commit -m "chore: update project last-updated dates" + git push + echo "pushed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Trigger deploy + if: steps.commit.outputs.pushed == 'true' + run: gh workflow run deploy-to-github-pages.yml + env: + GH_TOKEN: ${{ github.token }} diff --git a/src/components/projects/CardContainer.jsx b/src/components/projects/CardContainer.jsx index 2c4f86dd..9b253581 100644 --- a/src/components/projects/CardContainer.jsx +++ b/src/components/projects/CardContainer.jsx @@ -2,6 +2,7 @@ import { useState, useEffect } from "react"; import { useStore } from "@nanostores/react"; import { selectedTags } from "@stores/selectedTagsStore"; import { selectedProjectType } from "@stores/selectedProjectTypeStore"; +import { selectedSort } from "@stores/selectedSortStore"; import { getTagValues } from "@utils/getTags"; import { $projectData } from "@stores/projectSearchResultsStore"; import { $ecosystemData } from "@stores/ecosystemSearchResultsStore"; @@ -17,6 +18,8 @@ export default function CardContainer({ cardContent, contentType, maxLength, + //sortedTitlesMap, + sortOrder }) { const [visible, setVisible] = useState("relative"); const [order, setOrder] = useState(0); @@ -25,6 +28,7 @@ export default function CardContainer({ const ecosystemData = useStore($ecosystemData); const $selectedTags = useStore(selectedTags); const $selectedProjectType = useStore(selectedProjectType); + const $selectedSort = useStore(selectedSort); const tagsArray = getTagValues(tagsObj); let contentData = null; @@ -53,8 +57,14 @@ export default function CardContainer({ useEffect(() => { if (typeof window !== "undefined") { const matchingIndex = findMatchingIndex(contentData, title); - const itemOrder = - urlQuery === "" ? Math.floor(Math.random() * maxLength) : matchingIndex; + let itemOrder; + if (urlQuery !== "") { + itemOrder = matchingIndex; + } else if (sortOrder && Number.isInteger(sortOrder[$selectedSort])) { + itemOrder = sortOrder[$selectedSort]; + } else { + itemOrder = Math.floor(Math.random() * maxLength); + } setOrder(itemOrder); const isSearchMatch = @@ -83,6 +93,7 @@ export default function CardContainer({ contentData, $selectedProjectType, $selectedTags, + $selectedSort, contentType, projectType, ]); diff --git a/src/components/projects/FilterMenu.jsx b/src/components/projects/FilterMenu.jsx index 40ec8ce5..8ebfadc0 100644 --- a/src/components/projects/FilterMenu.jsx +++ b/src/components/projects/FilterMenu.jsx @@ -51,7 +51,7 @@ export default function FilterMenu({ uniqueTags }) {
- {/* This h3 and button are visible only on medium and larger screen sizes */} -
+

Filter by tag

- {/* This reset button is visible on small screens */} -
diff --git a/src/components/projects/ProjectTypeDropdown.jsx b/src/components/projects/ProjectTypeDropdown.jsx index 26b9e490..f2611214 100644 --- a/src/components/projects/ProjectTypeDropdown.jsx +++ b/src/components/projects/ProjectTypeDropdown.jsx @@ -31,7 +31,7 @@ const getOptionColorClasses = ({ selected, highlighted, disabled }) => { return classes; }; -const Option = React.forwardRef((props, ref) => { +export const Option = React.forwardRef((props, ref) => { return ( typeof fn === "function" ? fn(args) : fn; // I added the prop "multiple" to allow for multiple selections -const Select = React.forwardRef(function CustomSelect(props, ref) { +export const Select = React.forwardRef(function CustomSelect(props, ref) { return ( +

Sort by

+
+

Sort by

+ +
+ +
+ +
+ +
    + {SORT_OPTIONS.map(({ key, label }) => ( +
  • handleSortSelection(key)} + > +
    + {label} +
    +
  • + ))} +
+
+ ); +} diff --git a/src/components/projects/ToggleFilterMenuBtn.jsx b/src/components/projects/ToggleFilterMenuBtn.jsx index f622cdcf..199883cd 100644 --- a/src/components/projects/ToggleFilterMenuBtn.jsx +++ b/src/components/projects/ToggleFilterMenuBtn.jsx @@ -24,7 +24,7 @@ export default function ToggleFilterMenuBtn() { > {numFilters} -

Filter by tag

+

Filter by tag

); diff --git a/src/content.config.ts b/src/content.config.ts index e709284a..fa08c7af 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -12,6 +12,7 @@ const projectsCollection = defineCollection({ "OSSI project status": z.array(z.string()).optional(), "OSSI proposal link": z.string().optional(), "github link": z.string(), + "update_date": z.coerce.date().optional(), "documentation link": z.string(), "installation instructions link": z.string().optional(), "how to cite text": z.string().nullish(), diff --git a/src/content/projects/_project-template.md b/src/content/projects/_project-template.md index 70dcfe50..852fe8f7 100644 --- a/src/content/projects/_project-template.md +++ b/src/content/projects/_project-template.md @@ -8,6 +8,7 @@ project type: [OSSI - current, OSSI - alumni, Other] OSSI project status: [Proposed, Accepted, Active Development, Maintenance] OSSI proposal link: Required if your project is OSSI-funded. Preferred - upload the proposal as a PDF to `public/proposals` and provide the link in the format `../../proposals/PROPOSAL.pdf`. Other option - URL to the externally hosted proposal. github link: Required. Link to GitHub repository +#update_date - Do not set this manually. It is automatically populated by a scheduled GitHub Action from the repository's most recent push date, and used to power the "Last updated" sort option. documentation link: Required. Link to documentation - can be the same as the GitHub repo if the README is the documentation installation instructions link: Required. Link to installation instructions - can be the same as the GitHub repo preferred contact method: "Creates the link for the 'Reach out for help' button on the project page. Encouraged if there is a preferred way for users to reach out for help other than creating an issue in the project's GitHub repo. Examples: link to Image.sc forum, or an email in the format of mailto:email@example.com." diff --git a/src/content/projects/an-open-source-platform-for-single-molecule-localization.md b/src/content/projects/an-open-source-platform-for-single-molecule-localization.md index bbbc8950..d8882d72 100644 --- a/src/content/projects/an-open-source-platform-for-single-molecule-localization.md +++ b/src/content/projects/an-open-source-platform-for-single-molecule-localization.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Accepted] OSSI proposal link: ../../proposals/single_molecule_localization.pdf github link: https://github.com/TuragaLab/DECODE +update_date: 2023-06-22 documentation link: https://decode.readthedocs.io/ installation instructions link: https://github.com/TuragaLab/DECODE?tab=readme-ov-file#local-installation how to cite link: https://doi.org/10.1038/s41592-021-01236-x diff --git a/src/content/projects/bigstitcher-spark.md b/src/content/projects/bigstitcher-spark.md index 3ab391ca..2987dae5 100644 --- a/src/content/projects/bigstitcher-spark.md +++ b/src/content/projects/bigstitcher-spark.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/petascale_lightsheet.pdf github link: https://github.com/JaneliaSciComp/BigStitcher-Spark +update_date: 2026-08-01 documentation link: https://github.com/JaneliaSciComp/BigStitcher-Spark?tab=readme-ov-file#readme installation instructions link: https://github.com/JaneliaSciComp/BigStitcher-Spark/?tab=readme-ov-file#install preferred contact method: https://github.com/JaneliaSciComp/BigStitcher-Spark/issues diff --git a/src/content/projects/bigstitcher.md b/src/content/projects/bigstitcher.md index f6469cd8..3a2469fc 100644 --- a/src/content/projects/bigstitcher.md +++ b/src/content/projects/bigstitcher.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/petascale_lightsheet.pdf github link: https://github.com/preibischlab/bigstitcher/ +update_date: 2026-07-03 documentation link: https://imagej.net/plugins/bigstitcher/ installation instructions link: https://imagej.net/plugins/bigstitcher/#download how to cite link: https://doi.org/10.1038/s41592-019-0501-0 diff --git a/src/content/projects/bigstream.md b/src/content/projects/bigstream.md index 81be697a..25303624 100644 --- a/src/content/projects/bigstream.md +++ b/src/content/projects/bigstream.md @@ -6,6 +6,7 @@ preferred contact method: mailto:fleishmang@janelia.hhmi.org project type: [Other] OSSI proposal link: "" github link: https://github.com/JaneliaSciComp/bigstream +update_date: 2026-07-30 documentation link: https://github.com/JaneliaSciComp/bigstream installation instructions link: https://github.com/JaneliaSciComp/bigstream how to cite text: "Wang Y, Eddison M, Fleishman G, Weigert M, Xu S, Wang T, Rokicki K, Goina C, Henry FE, Lemire AL, Schmidt U, Yang H, Svoboda K, Myers EW, Saalfeld S, Korff W, Sternson SM, Tillberg PW. EASI-FISH for thick tissue defines lateral hypothalamus spatio-molecular organization. Cell. 2021 Dec 22;184(26):6361-6377.e24. doi: 10.1016/j.cell.2021.11.024. Epub 2021 Dec 6. PMID: 34875226." diff --git a/src/content/projects/bigwarp.md b/src/content/projects/bigwarp.md index 4ff29cad..4711903c 100644 --- a/src/content/projects/bigwarp.md +++ b/src/content/projects/bigwarp.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Accepted] OSSI proposal link: ../../proposals/bigwarp.pdf github link: https://github.com/saalfeldlab/bigwarp +update_date: 2026-07-29 documentation link: https://imagej.net/plugins/bigwarp installation instructions link: https://imagej.net/plugins/bigwarp#installation how to cite link: https://doi.org/10.1109/ISBI.2016.7493463 diff --git a/src/content/projects/catena.md b/src/content/projects/catena.md index 4735c3fd..4d757052 100644 --- a/src/content/projects/catena.md +++ b/src/content/projects/catena.md @@ -6,6 +6,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/catena.pdf github link: https://github.com/Mohinta2892/catena +update_date: 2026-05-18 documentation link: https://github.com/Mohinta2892/catena installation instructions link: https://github.com/Mohinta2892/catena preferred contact method: mailto:sm2667@cam.ac.uk diff --git a/src/content/projects/cellpose.md b/src/content/projects/cellpose.md index e12442c8..78e95e64 100644 --- a/src/content/projects/cellpose.md +++ b/src/content/projects/cellpose.md @@ -6,6 +6,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/suite2p_and_cellpose.pdf github link: https://github.com/MouseLand/cellpose +update_date: 2026-06-14 documentation link: https://cellpose.readthedocs.io/en/latest/ installation instructions link: https://github.com/MouseLand/cellpose?tab=readme-ov-file#installation how to cite link: https://doi.org/10.1038/s41592-020-01018-x diff --git a/src/content/projects/dacapo.md b/src/content/projects/dacapo.md index 0f320f73..6b4cb50a 100644 --- a/src/content/projects/dacapo.md +++ b/src/content/projects/dacapo.md @@ -6,6 +6,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/dacapo.pdf github link: https://github.com/janelia-cellmap/dacapo +update_date: 2026-04-24 documentation link: https://janelia-cellmap.github.io/dacapo/ installation instructions link: https://janelia-cellmap.github.io/dacapo/install.html preferred contact method: mailto:rhoadesj@janelia.hhmi.org diff --git a/src/content/projects/dvid.md b/src/content/projects/dvid.md index 772bd3bf..ef24817e 100644 --- a/src/content/projects/dvid.md +++ b/src/content/projects/dvid.md @@ -4,6 +4,7 @@ tagline: A dataservice for branched versioning of a variety of data types includ maintainer: Bill Katz project type: [Other] github link: https://github.com/janelia-flyem/dvid +update_date: 2026-07-25 documentation link: https://github.com/janelia-flyem/dvid/blob/master/README.md installation instructions link: https://github.com/janelia-flyem/dvid/blob/master/README.md#installation how to cite link: "https://doi.org/10.3389/fncir.2019.00005" diff --git a/src/content/projects/easi-fish-pipeline.md b/src/content/projects/easi-fish-pipeline.md index 12bf51fd..d86da330 100644 --- a/src/content/projects/easi-fish-pipeline.md +++ b/src/content/projects/easi-fish-pipeline.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/easifish_pipeline.pdf github link: https://github.com/JaneliaSciComp/multifish +update_date: 2026-04-15 documentation link: https://janeliascicomp.github.io/multifish/ installation instructions link: https://janeliascicomp.github.io/multifish/QuickStart.html how to cite link: https://doi.org/10.1016/j.cell.2021.11.024 diff --git a/src/content/projects/entrypoints.md b/src/content/projects/entrypoints.md index 5038400e..b57dfdc0 100644 --- a/src/content/projects/entrypoints.md +++ b/src/content/projects/entrypoints.md @@ -5,6 +5,7 @@ maintainer: Konrad Rokicki preferred contact method: https://github.com/JaneliaSciComp/entrypoints/issues project type: [Other] github link: https://github.com/JaneliaSciComp/entrypoints +update_date: 2021-01-03 documentation link: https://github.com/JaneliaSciComp/entrypoints installation instructions link: https://github.com/JaneliaSciComp/entrypoints?tab=readme-ov-file#installation how to cite link: https://github.com/JaneliaSciComp/entrypoints diff --git a/src/content/projects/facemap.md b/src/content/projects/facemap.md index c7f58fa1..1f209a67 100644 --- a/src/content/projects/facemap.md +++ b/src/content/projects/facemap.md @@ -5,6 +5,7 @@ maintainer: Carsen Stringer #project type - Required. Pick one of the three options - delete the other two. project type: [Other] github link: https://github.com/MouseLand/facemap +update_date: 2026-01-26 documentation link: https://facemap.readthedocs.io/en/latest/index.html installation instructions link: https://facemap.readthedocs.io/en/latest/installation.html how to cite text: "Syeda, A., Zhong, L., Tung, R., Long, W., Pachitariu, M.*, & Stringer, C.* (2024). Facemap: a framework for modeling neural activity based on orofacial tracking. Nature Neuroscience, 27(1), 187-195." diff --git a/src/content/projects/fileglancer.md b/src/content/projects/fileglancer.md index 4160e4ce..788cce54 100644 --- a/src/content/projects/fileglancer.md +++ b/src/content/projects/fileglancer.md @@ -8,6 +8,7 @@ project type: [Other] # OSSI project status: [Proposed, Accepted, Active Development, Maintenance] # OSSI proposal link: Required if your project is OSSI-funded. Preferred - upload the proposal as a PDF to `public/proposals` and provide the link in the format `../../proposals/PROPOSAL.pdf`. Other option - URL to the externally hosted proposal. github link: https://github.com/JaneliaSciComp/fileglancer +update_date: 2026-10-15 documentation link: https://fileglancer-docs.janelia.org installation instructions link: https://github.com/JaneliaSciComp/fileglancer#installation # preferred contact method: "Creates the link for the 'Reach out for help' button on the project page. Encouraged if there is a preferred way for users to reach out for help other than creating an issue in the project's GitHub repo. Examples: link to Image.sc forum, or an email in the format of mailto:email@example.com." diff --git a/src/content/projects/geff.md b/src/content/projects/geff.md index 6dbb73c9..5f360d8e 100644 --- a/src/content/projects/geff.md +++ b/src/content/projects/geff.md @@ -8,6 +8,7 @@ project type: [Other] # OSSI project status: [Proposed, Accepted, Active Development, Maintenance] # OSSI proposal link: Required if your project is OSSI-funded. Preferred - upload the proposal as a PDF to `public/proposals` and provide the link in the format `../../proposals/PROPOSAL.pdf`. Other option - URL to the externally hosted proposal. github link: https://github.com/live-image-tracking-tools/geff +update_date: 2026-07-29 documentation link: https://liveimagetrackingtools.org/geff/latest/ installation instructions link: https://liveimagetrackingtools.org/geff/latest/#installation # preferred contact method: "Creates the link for the 'Reach out for help' button on the project page. Encouraged if there is a preferred way for users to reach out for help other than creating an issue in the project's GitHub repo. Examples: link to Image.sc forum, or an email in the format of mailto:email@example.com." diff --git a/src/content/projects/hortacloud.md b/src/content/projects/hortacloud.md index 1722edc1..ac3b21c0 100644 --- a/src/content/projects/hortacloud.md +++ b/src/content/projects/hortacloud.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Maintenance] OSSI proposal link: ../../proposals/hortacloud.pdf github link: https://github.com/JaneliaSciComp/hortacloud +update_date: 2026-07-15 documentation link: https://hortacloud.janelia.org/docs/ installation instructions link: https://hortacloud.janelia.org/docs/administration/aws/ how to cite link: https://github.com/JaneliaSciComp/hortacloud diff --git a/src/content/projects/kilosort.md b/src/content/projects/kilosort.md index 72d4fff7..aa6051de 100644 --- a/src/content/projects/kilosort.md +++ b/src/content/projects/kilosort.md @@ -6,6 +6,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/kilosort.pdf github link: https://github.com/MouseLand/Kilosort +update_date: 2026-04-27 documentation link: https://kilosort.readthedocs.io/en/latest/ installation instructions link: https://github.com/MouseLand/Kilosort?tab=readme-ov-file#installation how to cite link: https://doi.org/10.1101/061481 diff --git a/src/content/projects/maru.md b/src/content/projects/maru.md index d1d0c9b2..75d8f141 100644 --- a/src/content/projects/maru.md +++ b/src/content/projects/maru.md @@ -5,6 +5,7 @@ maintainer: Konrad Rokicki preferred contact method: https://github.com/JaneliaSciComp/maru/issues project type: [Other] github link: https://github.com/JaneliaSciComp/maru +update_date: 2021-08-31 documentation link: https://github.com/JaneliaSciComp/maru installation instructions link: https://github.com/JaneliaSciComp/maru?tab=readme-ov-file#get-maru how to cite link: https://github.com/JaneliaSciComp/maru diff --git a/src/content/projects/motile-tracker.md b/src/content/projects/motile-tracker.md index 84bba597..1b9319b3 100644 --- a/src/content/projects/motile-tracker.md +++ b/src/content/projects/motile-tracker.md @@ -8,6 +8,7 @@ project type: [Other] # OSSI project status: [Proposed, Accepted, Active Development, Maintenance] # OSSI proposal link: Required if your project is OSSI-funded. Preferred - upload the proposal as a PDF to `public/proposals` and provide the link in the format `../../proposals/PROPOSAL.pdf`. Other option - URL to the externally hosted proposal. github link: https://github.com/funkelab/motile_tracker +update_date: 2026-08-03 documentation link: https://funkelab.github.io/motile_tracker/ installation instructions link: https://funkelab.github.io/motile_tracker/getting_started.html#installation # preferred contact method: "Creates the link for the 'Reach out for help' button on the project page. Encouraged if there is a preferred way for users to reach out for help other than creating an issue in the project's GitHub repo. Examples: link to Image.sc forum, or an email in the format of mailto:email@example.com." diff --git a/src/content/projects/neuronbridge.md b/src/content/projects/neuronbridge.md index e38d8423..2f31bd28 100644 --- a/src/content/projects/neuronbridge.md +++ b/src/content/projects/neuronbridge.md @@ -5,6 +5,7 @@ maintainer: Konrad Rokicki preferred contact method: mailto:neuronbridge@janelia.hhmi.org project type: [Other] github link: https://github.com/JaneliaSciComp/neuronbridge +update_date: 2026-07-27 documentation link: https://neuronbridge.janelia.org/help installation instructions link: https://github.com/JaneliaSciComp/neuronbridge/blob/master/docs/Development.md how to cite link: "https://doi.org/10.1186/s12859-024-05732-7" diff --git a/src/content/projects/neuvid.md b/src/content/projects/neuvid.md index 1b6dda0e..1bf112ed 100644 --- a/src/content/projects/neuvid.md +++ b/src/content/projects/neuvid.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Maintenance] OSSI proposal link: ../../proposals/neuvid.pdf github link: https://github.com/connectome-neuprint/neuVid +update_date: 2026-03-22 documentation link: https://github.com/connectome-neuprint/neuVid/tree/master/documentation installation instructions link: https://github.com/connectome-neuprint/neuVid?tab=readme-ov-file#usage-with-neuprint how to cite text: "Philip M. Hubbard, The newVid video generation system, https://zenodo.org/records/10805089" diff --git a/src/content/projects/nidaq.md b/src/content/projects/nidaq.md index c38063da..36aae5e1 100644 --- a/src/content/projects/nidaq.md +++ b/src/content/projects/nidaq.md @@ -5,6 +5,7 @@ maintainer: ben arthur #project type - Required. Pick one of the three options - delete the other two. project type: [Other] github link: https://github.com/JaneliaSciComp/NIDAQ.jl +update_date: 2026-02-08 documentation link: https://github.com/JaneliaSciComp/NIDAQ.jl?tab=readme-ov-file#national-instruments-data-acquisition-interface installation instructions link: https://github.com/JaneliaSciComp/NIDAQ.jl?tab=readme-ov-file#installation preferred contact method: mailto:arthurb@hhmi.org diff --git a/src/content/projects/ossi-website.md b/src/content/projects/ossi-website.md index c58d65f8..b64e4eb1 100644 --- a/src/content/projects/ossi-website.md +++ b/src/content/projects/ossi-website.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/ossi_website.pdf github link: https://github.com/JaneliaSciComp/ossi-website +update_date: 2026-03-25 documentation link: https://github.com/JaneliaSciComp/ossi-website installation instructions link: https://github.com/JaneliaSciComp/ossi-website?tab=readme-ov-file#how-to-addedit-your-software-project how to cite text: Cite the current deployment diff --git a/src/content/projects/paintera.md b/src/content/projects/paintera.md index 31b92c79..9646a603 100644 --- a/src/content/projects/paintera.md +++ b/src/content/projects/paintera.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/paintera.pdf github link: https://github.com/saalfeldlab/paintera +update_date: 2026-07-30 documentation link: https://github.com/saalfeldlab/paintera installation instructions link: https://github.com/saalfeldlab/paintera?tab=readme-ov-file#installation-and-usage how to cite link: https://zenodo.org/doi/10.5281/zenodo.3351562 diff --git a/src/content/projects/phase-diversity.md b/src/content/projects/phase-diversity.md index e358bcb4..150affe5 100755 --- a/src/content/projects/phase-diversity.md +++ b/src/content/projects/phase-diversity.md @@ -4,6 +4,7 @@ tagline: Software for analyzing phase diversity data for adaptive optics maintainer: Magdalena Schneider project type: [Other] github link: https://github.com/ceej640/PhaseDiversity +update_date: 2025-02-25 documentation link: https://github.com/ceej640/PhaseDiversity?tab=readme-ov-file#phase-diversity-wavefront-sensing-for-microscopy installation instructions link: https://github.com/ceej640/PhaseDiversity#Installation how to cite text: "Cite the corresponding paper: Johnson et al., Optica (2024)" diff --git a/src/content/projects/rastermap.md b/src/content/projects/rastermap.md index 444b4e95..483e3501 100644 --- a/src/content/projects/rastermap.md +++ b/src/content/projects/rastermap.md @@ -5,6 +5,7 @@ maintainer: Carsen Stringer #project type - Required. Pick one of the three options - delete the other two. project type: [Other] github link: https://github.com/mouseland/rastermap +update_date: 2025-04-04 documentation link: https://github.com/mouseland/rastermap?tab=readme-ov-file#rastermap installation instructions link: https://github.com/mouseland/rastermap?tab=readme-ov-file#installation # preferred contact method: "Creates the link for the 'Reach out for help' button on the project page. Encouraged if there is a preferred way for users to reach out for help other than creating an issue in the project's GitHub repo. Examples: link to Image.sc forum, or an email in the format of mailto:email@example.com." diff --git a/src/content/projects/rs-fish.md b/src/content/projects/rs-fish.md index 56e848de..986983e0 100644 --- a/src/content/projects/rs-fish.md +++ b/src/content/projects/rs-fish.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Maintenance] OSSI proposal link: ../../proposals/spatial_transcriptomics_tools.pdf github link: https://github.com/PreibischLab/RS-FISH +update_date: 2025-02-04 additional links array: [https://github.com/PreibischLab/RS-FISH-Spark] documentation link: https://github.com/PreibischLab/RS-FISH installation instructions link: https://github.com/PreibischLab/RS-FISH?tab=readme-ov-file#download diff --git a/src/content/projects/songexplorer.md b/src/content/projects/songexplorer.md index 503525c3..a8681b4b 100644 --- a/src/content/projects/songexplorer.md +++ b/src/content/projects/songexplorer.md @@ -5,6 +5,7 @@ maintainer: ben arthur #project type - Required. Pick one of the three options - delete the other two. project type: [Other] github link: https://github.com/JaneliaSciComp/SongExplorer +update_date: 2026-04-10 documentation link: https://github.com/JaneliaSciComp/SongExplorer?tab=readme-ov-file#table-of-contents installation instructions link: https://github.com/JaneliaSciComp/SongExplorer?tab=readme-ov-file#installation preferred contact method: mailto:arthurb@hhmi.org diff --git a/src/content/projects/stim.md b/src/content/projects/stim.md index 3867968c..d7895754 100644 --- a/src/content/projects/stim.md +++ b/src/content/projects/stim.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/spatial_transcriptomics_tools.pdf github link: https://github.com/PreibischLab/STIM +update_date: 2025-05-27 documentation link: https://github.com/PreibischLab/STIM/wiki how to cite link: https://doi.org/10.1016/j.cels.2025.101264 how to cite text: "S. Preibisch, N. Karaiskos, N.Rajewsky, Scalable image-based visualization and alignment of spatial transcriptomics datasets. Cell Systems, Vol. 16, No. 5 (2025)." diff --git a/src/content/projects/stitching-spark.md b/src/content/projects/stitching-spark.md index f2483f6f..b460b18d 100644 --- a/src/content/projects/stitching-spark.md +++ b/src/content/projects/stitching-spark.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Accepted] OSSI proposal link: ../../proposals/petascale_lightsheet.pdf github link: https://github.com/saalfeldlab/stitching-spark +update_date: 2023-10-17 documentation link: https://github.com/saalfeldlab/stitching-spark installation instructions link: https://github.com/saalfeldlab/stitching-spark?tab=readme-ov-file#usage how to cite link: https://doi.org/10.1126/science.aau8302 diff --git a/src/content/projects/suite2p.md b/src/content/projects/suite2p.md index 94c2a52c..dc3c7842 100644 --- a/src/content/projects/suite2p.md +++ b/src/content/projects/suite2p.md @@ -6,6 +6,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/suite2p_and_cellpose.pdf github link: https://github.com/MouseLand/suite2p +update_date: 2026-07-01 documentation link: https://suite2p.readthedocs.io/en/latest/ installation instructions link: https://github.com/MouseLand/suite2p?tab=readme-ov-file#local-installation how to cite link: https://doi.org/10.1101/061507 diff --git a/src/content/projects/traccuracy.md b/src/content/projects/traccuracy.md index 7e73d183..e852027a 100644 --- a/src/content/projects/traccuracy.md +++ b/src/content/projects/traccuracy.md @@ -8,6 +8,7 @@ project type: [Other] # OSSI project status: [Proposed, Accepted, Active Development, Maintenance] # OSSI proposal link: Required if your project is OSSI-funded. Preferred - upload the proposal as a PDF to `public/proposals` and provide the link in the format `../../proposals/PROPOSAL.pdf`. Other option - URL to the externally hosted proposal. github link: https://github.com/live-image-tracking-tools/traccuracy +update_date: 2026-07-27 documentation link: https://traccuracy.readthedocs.io/en/latest/ installation instructions link: https://traccuracy.readthedocs.io/en/latest/#installation # preferred contact method: "Creates the link for the 'Reach out for help' button on the project page. Encouraged if there is a preferred way for users to reach out for help other than creating an issue in the project's GitHub repo. Examples: link to Image.sc forum, or an email in the format of mailto:email@example.com." diff --git a/src/content/projects/trainspikingnet.md b/src/content/projects/trainspikingnet.md index 8aa61911..bf34d35d 100755 --- a/src/content/projects/trainspikingnet.md +++ b/src/content/projects/trainspikingnet.md @@ -5,6 +5,7 @@ maintainer: ben arthur #project type - Required. Pick one of the three options - delete the other two. project type: [Other] github link: https://github.com/SpikingNetwork/TrainSpikingNet.jl +update_date: 2024-08-29 documentation link: https://github.com/SpikingNetwork/TrainSpikingNet.jl?tab=readme-ov-file installation instructions link: https://github.com/SpikingNetwork/TrainSpikingNet.jl?tab=readme-ov-file#installation preferred contact method: mailto:arthurb@hhmi.org diff --git a/src/content/projects/video-annotation-library.md b/src/content/projects/video-annotation-library.md index b76f3fe5..3255ea52 100644 --- a/src/content/projects/video-annotation-library.md +++ b/src/content/projects/video-annotation-library.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Active Development] OSSI proposal link: ../../proposals/video_annotation_library.pdf github link: https://github.com/JaneliaSciComp/videoAnnotation +update_date: 2026-07-28 documentation link: https://github.com/JaneliaSciComp/videoAnnotation installation instructions link: https://github.com/JaneliaSciComp/videoAnnotation how to cite link: https://github.com/JaneliaSciComp/videoAnnotation diff --git a/src/content/projects/vvd-viewer.md b/src/content/projects/vvd-viewer.md index d6b30276..29731711 100644 --- a/src/content/projects/vvd-viewer.md +++ b/src/content/projects/vvd-viewer.md @@ -7,6 +7,7 @@ project type: [OSSI - current] OSSI project status: [Maintenance] OSSI proposal link: ../../proposals/vvdviewer.pdf github link: https://github.com/JaneliaSciComp/VVDViewer +update_date: 2026-07-15 documentation link: https://github.com/JaneliaSciComp/VVDViewer/wiki installation instructions link: https://github.com/JaneliaSciComp/VVDViewer?tab=readme-ov-file#building-vvdviewer how to cite link: https://doi.org/10.5281/zenodo.5841616 diff --git a/src/content/projects/x2s3.md b/src/content/projects/x2s3.md index f04e340a..68d206e8 100644 --- a/src/content/projects/x2s3.md +++ b/src/content/projects/x2s3.md @@ -5,6 +5,7 @@ maintainer: Konrad Rokicki preferred contact method: https://github.com/JaneliaSciComp/x2s3/issues project type: [Other] github link: https://github.com/JaneliaSciComp/x2s3 +update_date: 2026-07-30 documentation link: https://github.com/JaneliaSciComp/x2s3 installation instructions link: https://github.com/JaneliaSciComp/x2s3?tab=readme-ov-file#running how to cite link: https://github.com/JaneliaSciComp/x2s3 diff --git a/src/content/projects/zarrcade.md b/src/content/projects/zarrcade.md index d9e4d1c8..cc75c9c8 100644 --- a/src/content/projects/zarrcade.md +++ b/src/content/projects/zarrcade.md @@ -5,6 +5,7 @@ maintainer: Konrad Rokicki preferred contact method: https://github.com/JaneliaSciComp/zarrcade/issues project type: [Other] github link: https://github.com/JaneliaSciComp/zarrcade +update_date: 2026-07-07 documentation link: https://github.com/JaneliaSciComp/zarrcade installation instructions link: https://github.com/JaneliaSciComp/zarrcade?tab=readme-ov-file#getting-started how to cite link: https://github.com/JaneliaSciComp/zarrcade diff --git a/src/pages/projects/index.astro b/src/pages/projects/index.astro index db0d5c88..5e83cfdb 100644 --- a/src/pages/projects/index.astro +++ b/src/pages/projects/index.astro @@ -14,6 +14,7 @@ import ProjectCount from "@components/projects/ProjectCount"; import ProjectTypeBtns from "@components/projects/ProjectTypeBtns"; import ProjectTypeDropdown from "@components/projects/ProjectTypeDropdown"; import SearchInput from "@components/projects/SearchInput"; +import SortMenu from "@components/projects/SortMenu"; import Tag from "@components/projects/Tag.astro"; import ToggleFilterMenuBtn from "@components/projects/ToggleFilterMenuBtn"; @@ -25,6 +26,29 @@ const allProjects = await getCollection("projects"); // uniqueTags is an object where keys = unique tag categories, values = unique tags within a category, both across allProjects. All lowercase. // used to populate the filter menu const uniqueTags = getTagKeysAndValues(allProjects); + +// all project titles sorted alphabetically; used to position cards when the "Alphabetical" sort is selected +const sortedTitles = allProjects + .map((content) => content.data.title) + .sort((a, b) => a.localeCompare(b)); + +// project titles sorted by update_date, oldest-to-newest; projects with no +// known update_date are left at the end regardless of sort direction +const undatedTitles = allProjects + .filter((content) => !content.data.update_date) + .map((content) => content.data.title); +const titlesByUpdateDateAsc = allProjects + .filter((content) => content.data.update_date) + .sort((a, b) => a.data.update_date - b.data.update_date) + .map((content) => content.data.title); + +// maps each selectable "sort by" key to its pre-sorted array of titles; +// consumed by CardContainer to position each card via CSS order +const sortedTitlesMap = { + alphabetical: sortedTitles, + "updated-newest": [...titlesByUpdateDateAsc].reverse().concat(undatedTitles), + "updated-oldest": [...titlesByUpdateDateAsc, ...undatedTitles], +}; --- @@ -41,8 +65,12 @@ const uniqueTags = getTagKeysAndValues(allProjects); >
- + +
+ + +
@@ -59,11 +87,17 @@ const uniqueTags = getTagKeysAndValues(allProjects); client:only="react" />
{ allProjects.map((content) => { const tagsObj = getTagKeysAndValues(content); + const title=content.data.title; + const sortOrder = { + alphabetical: sortedTitlesMap.alphabetical.indexOf(title), + "updated-newest": sortedTitlesMap["updated-newest"].indexOf(title), + "updated-oldest": sortedTitlesMap["updated-oldest"].indexOf(title), + }; return ( {content.data["image file"] ? ( diff --git a/src/stores/selectedSortStore.js b/src/stores/selectedSortStore.js new file mode 100644 index 00000000..7fe04c97 --- /dev/null +++ b/src/stores/selectedSortStore.js @@ -0,0 +1,39 @@ +import { atom, onMount } from "nanostores"; + +export const selectedSort = atom(""); + +onMount(selectedSort, () => { + const currentSort = selectedSort.get(); + let urlSort = ""; + if (typeof window !== "undefined") { + const searchParams = new URLSearchParams(window.location.search); + urlSort = searchParams.get("sort") || ""; + } + if (currentSort === "" && urlSort !== "") { + selectedSort.set(urlSort); + } +}); + +function updateSortSearchParams(updatedSort) { + // update URL search params + if (typeof window !== "undefined") { + const currentUrl = new URL(window.location); + const searchParams = new URLSearchParams(currentUrl.search); + + if (updatedSort) { + searchParams.set("sort", updatedSort); + } else { + searchParams.delete("sort"); + } + + // Use history.pushState to update the URL without reloading the page + window.history.pushState({}, "", `${currentUrl.pathname}?${searchParams}`); + } +} + +export function handleSortSelection(sort) { + const prevSort = selectedSort.get(); + const updatedSort = prevSort === sort ? "" : sort; + selectedSort.set(updatedSort); + updateSortSearchParams(updatedSort); +}