diff --git a/.gitignore b/.gitignore
index 355b808..e76bf9d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,4 +47,7 @@ next-env.d.ts
# project sample files
_data/*-sample.yml
-_data/*-sample.json
\ No newline at end of file
+_data/*-sample.json
+
+# project images (generated during build)
+public/project-images/
\ No newline at end of file
diff --git a/_data/projects.yml b/_data/projects.yml
index 12efa79..b23fd7b 100644
--- a/_data/projects.yml
+++ b/_data/projects.yml
@@ -14,8 +14,8 @@
language: C++
created_at: "2016-10-02T13:07:18Z"
updated_at: "2025-07-06T01:50:41Z"
- og_image: >-
- https://opengraph.githubassets.com/bab195a2c9f578e288086c0612d70ed78132ec633de95b096bc80e9da734d175/InDieTasten/Connect4
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: Slanted-Prime-Sudoku
status: done
@@ -31,8 +31,8 @@
language: C++
created_at: "2016-12-22T11:36:19Z"
updated_at: "2025-07-06T01:47:50Z"
- og_image: >-
- https://opengraph.githubassets.com/cc0ead899e605442efeabc05d0b56f87004ba19526134082c84ef0d4c04e6917/InDieTasten/Slanted-Prime-Sudoku
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: DevTease.MicrosoftLoggingExtensions
status: done
@@ -50,8 +50,8 @@
language: HTML
created_at: "2018-09-10T04:19:02Z"
updated_at: "2025-07-06T01:45:54Z"
- og_image: >-
- https://opengraph.githubassets.com/96ea5396d3579b33372d1050766d222601ec943a9bd335e23cfc7d38e8cb7bea/InDieTasten/DevTease.MicrosoftLoggingExtensions
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: DevTease.AspNetIntegrationTests
status: done
@@ -69,8 +69,8 @@
language: HTML
created_at: "2018-09-26T10:11:27Z"
updated_at: "2025-07-06T01:44:46Z"
- og_image: >-
- https://opengraph.githubassets.com/7f2c3533f2af804ad1e203fab018cfb52ef73b993417617b8a90fee55f2ad6a7/InDieTasten/DevTease.AspNetIntegrationTests
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: DevTease.Plugins
status: done
@@ -85,8 +85,8 @@
language: C#
created_at: "2019-02-03T19:59:43Z"
updated_at: "2025-07-06T01:49:01Z"
- og_image: >-
- https://opengraph.githubassets.com/f0780af1b0e6d327103fc236ad7c94879a736e241b7272089ae99828b6701fbb/InDieTasten/DevTease.Plugins
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: Authenticated-Integration-Testing
status: done
@@ -106,8 +106,8 @@
language: C#
created_at: "2019-10-17T16:27:01Z"
updated_at: "2025-07-06T01:38:23Z"
- og_image: >-
- https://opengraph.githubassets.com/3e8e2728e654a431b9a29e21cac7fff48e25bf96f03e7375d2110f731cfd9d59/InDieTasten/Authenticated-Integration-Testing
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: Sars-Cov2-Necessity-Screening
status: done
@@ -127,8 +127,8 @@
language: TypeScript
created_at: "2020-03-21T09:20:23Z"
updated_at: "2025-07-06T01:41:22Z"
- og_image: >-
- https://opengraph.githubassets.com/23c45f047278fc73ec19d295c271317a09f01d410e4ef4b351fb4677cf05f2e0/InDieTasten/Sars-Cov2-Necessity-Screening
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: TerminalRTX
status: done
@@ -146,8 +146,8 @@
language: C#
created_at: "2020-05-23T23:54:11Z"
updated_at: "2025-07-06T00:49:42Z"
- og_image: >-
- https://opengraph.githubassets.com/c60b39b5be4d1b426ed32941ae53e2b58de1efc57dae6ec359d65ab30cee1b6f/InDieTasten/TerminalRTX
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: DigitalDepot
status: done
@@ -163,8 +163,8 @@
language: JavaScript
created_at: "2021-07-15T17:09:41Z"
updated_at: "2025-07-06T01:42:45Z"
- og_image: >-
- https://opengraph.githubassets.com/c6c7c22ebfaf993da0e197a5f7c1b384b13320aa294028c022c8d2c690e6362b/InDieTasten/DigitalDepot
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: tictactoe-rs
status: done
@@ -184,8 +184,8 @@
language: Rust
created_at: "2023-03-11T00:38:26Z"
updated_at: "2025-07-06T01:41:39Z"
- og_image: >-
- https://opengraph.githubassets.com/ac747458f72be7a94db597ac12dc4397241c0ac0e1e849ad6bd10b14ccc02781/InDieTasten/tictactoe-rs
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: alternate-screen-buffer-demo
status: done
@@ -201,8 +201,8 @@
language: Rust
created_at: "2024-02-10T01:32:23Z"
updated_at: "2025-07-06T01:27:37Z"
- og_image: >-
- https://opengraph.githubassets.com/48ddb6e5df82777e22b25c8f519e9c51a04c683418668be02d44b8bd9fef9d0b/InDieTasten/alternate-screen-buffer-demo
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: mouse-tracking-demo
status: done
@@ -221,8 +221,8 @@
language: Rust
created_at: "2024-02-14T02:50:22Z"
updated_at: "2025-07-06T01:39:40Z"
- og_image: >-
- https://opengraph.githubassets.com/7d8550a517e2718c57bc524b848eef5284a9239ca91384e4610ae6e639df082e/InDieTasten/mouse-tracking-demo
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: ANSI-mate
status: done
@@ -244,7 +244,8 @@
language: Lua
created_at: "2024-05-05T11:59:56Z"
updated_at: "2025-07-06T01:38:32Z"
- og_image: https://repository-images.githubusercontent.com/796251486/5ffc646c-85db-450e-9018-8ce2b86de90d
+ og_image: /project-images/ANSI-mate-5ffc646c-85db-450e-9018-8ce2b86de90d.gif
+ custom_image_url: https://repository-images.githubusercontent.com/796251486/5ffc646c-85db-450e-9018-8ce2b86de90d
archived: false
- name: mandel-lua
status: done
@@ -268,7 +269,8 @@
language: Lua
created_at: "2024-08-09T12:22:43Z"
updated_at: "2025-08-29T18:31:33Z"
- og_image: https://repository-images.githubusercontent.com/840310203/9323cb94-15c0-4e2e-8535-8dde50fe5b88
+ og_image: /project-images/mandel-lua-9323cb94-15c0-4e2e-8535-8dde50fe5b88.png
+ custom_image_url: https://repository-images.githubusercontent.com/840310203/9323cb94-15c0-4e2e-8535-8dde50fe5b88
archived: false
- name: superstitious-gh
status: done
@@ -290,8 +292,8 @@
language: JavaScript
created_at: "2025-01-29T10:06:33Z"
updated_at: "2025-08-29T22:51:20Z"
- og_image: >-
- https://opengraph.githubassets.com/da7782f8135396d1751452e56cd616ab713d10da728323093bd2b4da08c8f6f3/InDieTasten/superstitious-gh
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: InDieTasten
status: in-progress
@@ -303,8 +305,8 @@
language: null
created_at: "2020-09-22T20:47:06Z"
updated_at: "2025-05-26T15:39:03Z"
- og_image: >-
- https://opengraph.githubassets.com/10b2a430b30b71c848dd2ea6e1d657f0b11d455b42cf4e6f255e9f3f08646e4a/InDieTasten/InDieTasten
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: pfand
status: in-progress
@@ -323,8 +325,8 @@
language: Rust
created_at: "2024-07-24T16:26:12Z"
updated_at: "2025-04-08T18:34:29Z"
- og_image: >-
- https://opengraph.githubassets.com/7ea257cc486c2c9b21e45af55425bb2eba6dd4e9e49b51d3c75ef9b2dd31c591/InDieTasten/pfand
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: k8s-bug
status: in-progress
@@ -336,8 +338,8 @@
language: Shell
created_at: "2025-03-12T16:45:41Z"
updated_at: "2025-03-12T16:45:47Z"
- og_image: >-
- https://opengraph.githubassets.com/4dce0556bd899219d31af8f88c530f7e237abab159ffdbcb01586b0a9b11dfb6/InDieTasten/k8s-bug
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: OpenHour
status: in-progress
@@ -349,8 +351,8 @@
language: null
created_at: "2025-07-12T02:22:37Z"
updated_at: "2025-07-12T02:22:38Z"
- og_image: >-
- https://opengraph.githubassets.com/ab9a9cb5ca669adcec4d2f326191284fe3f2daaaac978334d280b1923cd4e531/InDieTasten/OpenHour
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: acc-engineering-agent
status: in-progress
@@ -364,8 +366,8 @@
language: null
created_at: "2025-07-13T10:56:45Z"
updated_at: "2025-07-13T10:56:45Z"
- og_image: >-
- https://opengraph.githubassets.com/d67d59e1c346b2f27d2c113cf5fa6cd9ef5d989a7e45738d5b0b7194b7c0fb3c/InDieTasten/acc-engineering-agent
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: "--EXP-old-"
status: abandoned
@@ -377,8 +379,8 @@
language: C++
created_at: "2014-03-24T17:32:25Z"
updated_at: "2023-01-28T21:20:54Z"
- og_image: >-
- https://opengraph.githubassets.com/c3fd98e44e7d55e8ad9d13b2ca76abd35692c9c5f490da574526cc4288526317/InDieTasten/--EXP-old-
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: "--libavc-test"
status: abandoned
@@ -390,8 +392,8 @@
language: C++
created_at: "2014-09-21T16:36:20Z"
updated_at: "2023-01-28T21:20:54Z"
- og_image: >-
- https://opengraph.githubassets.com/c3fd98e44e7d55e8ad9d13b2ca76abd35692c9c5f490da574526cc4288526317/InDieTasten/--libavc-test
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: "--CodeX"
status: abandoned
@@ -403,8 +405,8 @@
language: Lua
created_at: "2014-11-21T22:50:29Z"
updated_at: "2023-01-28T21:20:54Z"
- og_image: >-
- https://opengraph.githubassets.com/c3fd98e44e7d55e8ad9d13b2ca76abd35692c9c5f490da574526cc4288526317/InDieTasten/--CodeX
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: "--2k16"
status: abandoned
@@ -416,8 +418,8 @@
language: HTML
created_at: "2015-12-31T23:05:18Z"
updated_at: "2023-01-28T21:20:54Z"
- og_image: >-
- https://opengraph.githubassets.com/c3fd98e44e7d55e8ad9d13b2ca76abd35692c9c5f490da574526cc4288526317/InDieTasten/--2k16
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: "--Formal"
status: abandoned
@@ -429,8 +431,8 @@
language: C++
created_at: "2016-05-05T10:20:27Z"
updated_at: "2023-01-28T21:20:54Z"
- og_image: >-
- https://opengraph.githubassets.com/c3fd98e44e7d55e8ad9d13b2ca76abd35692c9c5f490da574526cc4288526317/InDieTasten/--Formal
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: REEL
status: abandoned
@@ -442,7 +444,8 @@
language: Lua
created_at: "2016-10-01T13:35:15Z"
updated_at: "2023-01-28T20:40:31Z"
- og_image: https://opengraph.githubassets.com/073c830c82460c54e6b207c421a99ad8fa9f4931b7d4bf7604410f3ad275d2cf/InDieTasten/REEL
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: b0tnet
status: abandoned
@@ -458,7 +461,8 @@
language: TypeScript
created_at: "2017-03-15T19:20:41Z"
updated_at: "2023-10-11T23:56:41Z"
- og_image: https://repository-images.githubusercontent.com/85112149/e96a4f00-fa8a-11e9-8f81-fa2ae4bffe68
+ og_image: /project-images/b0tnet-e96a4f00-fa8a-11e9-8f81-fa2ae4bffe68.png
+ custom_image_url: https://repository-images.githubusercontent.com/85112149/e96a4f00-fa8a-11e9-8f81-fa2ae4bffe68
archived: true
- name: Top-Down-Heist
status: abandoned
@@ -474,8 +478,8 @@
language: C#
created_at: "2017-12-17T02:15:37Z"
updated_at: "2023-01-28T06:31:07Z"
- og_image: >-
- https://opengraph.githubassets.com/ce3d50dab3c3bd0de7f1b9c7be0362df12ef7d65e0893df97b33b90bde80809b/InDieTasten/Top-Down-Heist
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: indie-classes
status: abandoned
@@ -487,8 +491,8 @@
language: Lua
created_at: "2018-01-06T18:37:16Z"
updated_at: "2018-01-06T18:38:25Z"
- og_image: >-
- https://opengraph.githubassets.com/5195963ea8fea5e48834f1b7c6a5f83d6b6ec5f0125e736e410ba32363d2c871/InDieTasten/indie-classes
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: indie-ml
status: abandoned
@@ -500,8 +504,8 @@
language: Lua
created_at: "2018-01-06T18:39:44Z"
updated_at: "2023-01-28T15:41:45Z"
- og_image: >-
- https://opengraph.githubassets.com/8448a3bf506b4cc143b0c7ae2aa3619e6ebc8d403c6299b467c60b33756a7be4/InDieTasten/indie-ml
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: CSharp.lua
status: abandoned
@@ -517,8 +521,8 @@
language: C#
created_at: "2018-04-21T12:41:43Z"
updated_at: "2023-01-28T15:41:45Z"
- og_image: >-
- https://opengraph.githubassets.com/8448a3bf506b4cc143b0c7ae2aa3619e6ebc8d403c6299b467c60b33756a7be4/InDieTasten/CSharp.lua
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: CC-CSharp
status: abandoned
@@ -530,8 +534,8 @@
language: null
created_at: "2018-04-21T14:50:29Z"
updated_at: "2023-01-28T15:41:45Z"
- og_image: >-
- https://opengraph.githubassets.com/8448a3bf506b4cc143b0c7ae2aa3619e6ebc8d403c6299b467c60b33756a7be4/InDieTasten/CC-CSharp
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: RetailPro.Backend
status: abandoned
@@ -543,8 +547,8 @@
language: C#
created_at: "2018-09-27T23:35:57Z"
updated_at: "2024-10-13T17:32:36Z"
- og_image: >-
- https://opengraph.githubassets.com/dec424542de9106a21663fe241c99ac13142eb8251ae987e4867d77cd32a514f/InDieTasten/RetailPro.Backend
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: BadRazorInjectionExample
status: abandoned
@@ -556,8 +560,8 @@
language: C#
created_at: "2018-09-29T13:28:06Z"
updated_at: "2023-06-28T06:34:04Z"
- og_image: >-
- https://opengraph.githubassets.com/61929546e133c92319867075331df73ad6293d80d17e339d516b2e4105cd2302/InDieTasten/BadRazorInjectionExample
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: DevTease.VueTs
status: abandoned
@@ -573,8 +577,8 @@
language: Vue
created_at: "2018-10-26T15:51:57Z"
updated_at: "2023-06-28T06:25:23Z"
- og_image: >-
- https://opengraph.githubassets.com/0ce0e4acc4fd354a0a137d335672a4b82d4db94c40fc31f4ff2c9bbc1d325ad1/InDieTasten/DevTease.VueTs
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: DevTease.DI
status: abandoned
@@ -586,8 +590,8 @@
language: C#
created_at: "2019-01-22T19:18:47Z"
updated_at: "2020-12-20T11:25:24Z"
- og_image: >-
- https://opengraph.githubassets.com/54bff6fe6f739a46969e2961fb25a0d45ed190f44995c41325764ab0a623d3ee/InDieTasten/DevTease.DI
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: breadstone
status: abandoned
@@ -604,7 +608,8 @@
language: HTML
created_at: "2019-07-19T19:14:26Z"
updated_at: "2022-08-01T10:11:25Z"
- og_image: https://repository-images.githubusercontent.com/197829352/8b0d6880-c46a-11e9-903b-6500e4a61ad2
+ og_image: /project-images/breadstone-8b0d6880-c46a-11e9-903b-6500e4a61ad2.png
+ custom_image_url: https://repository-images.githubusercontent.com/197829352/8b0d6880-c46a-11e9-903b-6500e4a61ad2
archived: false
- name: terminal-damage
status: abandoned
@@ -616,8 +621,8 @@
language: HTML
created_at: "2020-03-19T20:32:33Z"
updated_at: "2021-11-30T08:06:20Z"
- og_image: >-
- https://opengraph.githubassets.com/4838fded7665b4bb8fbe3363342e1fafdf39ae5043a9f3582896b295f17662b7/InDieTasten/terminal-damage
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: TextAdventure.NET
status: abandoned
@@ -633,8 +638,8 @@
language: C#
created_at: "2020-08-19T19:48:19Z"
updated_at: "2025-07-06T01:28:41Z"
- og_image: >-
- https://opengraph.githubassets.com/3b5246ea36117acb0c762cc7cdc0c045dbf43e6d114e527a44f72060353392f2/InDieTasten/TextAdventure.NET
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: SpaceRace
status: abandoned
@@ -648,8 +653,8 @@
language: C#
created_at: "2020-11-03T19:40:16Z"
updated_at: "2025-07-06T01:25:33Z"
- og_image: >-
- https://opengraph.githubassets.com/cb73277f82ff5a8f8bc9b7f6399de4fd55541e9c9aa0c2510361fcb5a72ea8dd/InDieTasten/SpaceRace
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: VirtualDev
status: abandoned
@@ -667,8 +672,8 @@
language: C#
created_at: "2021-01-24T11:06:26Z"
updated_at: "2025-07-06T01:26:04Z"
- og_image: >-
- https://opengraph.githubassets.com/93c3c92df8bc9dcc1966c578a76306a75f6b7d9df64ee62f90090aa92e363727/InDieTasten/VirtualDev
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: CliSpectrogram.NET
status: abandoned
@@ -680,8 +685,8 @@
language: C#
created_at: "2021-01-29T18:04:59Z"
updated_at: "2024-04-21T00:53:45Z"
- og_image: >-
- https://opengraph.githubassets.com/16724d3abfd4f9baea468f3e870de7a53ca93c90e5a2cd17ab96fd9d6742c396/InDieTasten/CliSpectrogram.NET
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: DevTease.Rasa
status: abandoned
@@ -693,8 +698,8 @@
language: Python
created_at: "2021-02-09T20:53:57Z"
updated_at: "2021-02-09T20:55:13Z"
- og_image: >-
- https://opengraph.githubassets.com/2fd7ce10cfbfa42752ecbd803911b5ee6e93c9d8beea6311c2796075e3123cac/InDieTasten/DevTease.Rasa
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: WikiWebhookTestRepo
status: abandoned
@@ -708,8 +713,8 @@
language: null
created_at: "2021-07-27T10:37:08Z"
updated_at: "2021-07-27T10:37:08Z"
- og_image: >-
- https://opengraph.githubassets.com/b2ef3529ce0618b45e991908d203c0571e75d9095ec7f469d493a7c6fb43fb33/InDieTasten/WikiWebhookTestRepo
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: CoinTale
status: abandoned
@@ -721,8 +726,8 @@
language: C#
created_at: "2022-01-22T23:02:06Z"
updated_at: "2023-01-28T02:25:57Z"
- og_image: >-
- https://opengraph.githubassets.com/eb8b9778ba864646b9cdee0515512835d5e798b420251852bf0689e2c4a6df8f/InDieTasten/CoinTale
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: operator-game
status: abandoned
@@ -738,8 +743,8 @@
language: Rust
created_at: "2023-10-09T01:50:19Z"
updated_at: "2025-07-06T01:27:03Z"
- og_image: >-
- https://opengraph.githubassets.com/171265910da829c16988fb40699baadfb48613faa1efa1cb4ec80dacc0364d31/InDieTasten/operator-game
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: true
- name: astats-fast
status: abandoned
@@ -751,8 +756,8 @@
language: null
created_at: "2024-05-05T11:54:03Z"
updated_at: "2024-05-24T03:28:14Z"
- og_image: >-
- https://opengraph.githubassets.com/40427b9fa7fd4bd63800262e35132798348ea395deb2d696b60994733df1d2e9/InDieTasten/astats-fast
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: flat-game
status: abandoned
@@ -764,8 +769,8 @@
language: null
created_at: "2024-05-05T11:56:03Z"
updated_at: "2024-05-05T11:56:07Z"
- og_image: >-
- https://opengraph.githubassets.com/038a633786e8da1f68a93c02dc569695a8db7a60d03fe478044c50e412136aa6/InDieTasten/flat-game
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: connect-ice
status: abandoned
@@ -777,8 +782,8 @@
language: null
created_at: "2024-05-05T11:57:10Z"
updated_at: "2024-05-05T11:57:14Z"
- og_image: >-
- https://opengraph.githubassets.com/24fecfa1d262af239bd5686212ed85feac1d121487ecb8dd2c75031ed040741d/InDieTasten/connect-ice
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: Novel-Now
status: abandoned
@@ -790,8 +795,8 @@
language: null
created_at: "2024-05-05T11:58:59Z"
updated_at: "2024-05-05T11:59:03Z"
- og_image: >-
- https://opengraph.githubassets.com/dd7fdd71dcce6f6caf5927ce1b3d5f5fa942baf102f68e4744bde5703e0f244f/InDieTasten/Novel-Now
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: Leergut-Simulator
status: abandoned
@@ -805,8 +810,8 @@
language: null
created_at: "2024-05-05T12:00:24Z"
updated_at: "2024-05-05T12:00:27Z"
- og_image: >-
- https://opengraph.githubassets.com/abfa86bd95787fe3dd0e46ff12aead991f8cdd0558930c766db601e67f2a6da9/InDieTasten/Leergut-Simulator
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: cli-lighthouse-viewer
status: abandoned
@@ -818,8 +823,8 @@
language: null
created_at: "2024-05-05T12:01:00Z"
updated_at: "2024-05-05T12:01:04Z"
- og_image: >-
- https://opengraph.githubassets.com/577b4abadf36d62d560c3f9ff1bee62e5ba86a4e9af9857d6f9d310c9b9f0320/InDieTasten/cli-lighthouse-viewer
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: EduPlex
status: abandoned
@@ -831,8 +836,8 @@
language: null
created_at: "2024-05-05T12:01:25Z"
updated_at: "2024-05-05T12:01:29Z"
- og_image: >-
- https://opengraph.githubassets.com/1dbac8ed912fde031f27ea92cc394675937bff6868d04fba0df671b94aa70c53/InDieTasten/EduPlex
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: Shady-Comms-Inc
status: abandoned
@@ -844,8 +849,8 @@
language: null
created_at: "2024-05-05T19:32:56Z"
updated_at: "2024-05-05T19:33:00Z"
- og_image: >-
- https://opengraph.githubassets.com/5b97838c93719eb0fdb98d87086bad38b163a0382b894c29b3dcb5276afe5cb0/InDieTasten/Shady-Comms-Inc
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: gh-extraordinaire-farm
status: abandoned
@@ -859,8 +864,8 @@
language: null
created_at: "2024-05-24T01:34:56Z"
updated_at: "2024-05-24T01:34:56Z"
- og_image: >-
- https://opengraph.githubassets.com/263debf3ecb6d5a8d1427466190319c54062e54c1f788990a6e297e9d5f3541a/InDieTasten/gh-extraordinaire-farm
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: gh-sine-activity
status: abandoned
@@ -872,8 +877,8 @@
language: null
created_at: "2024-05-24T01:36:29Z"
updated_at: "2024-05-24T01:36:29Z"
- og_image: >-
- https://opengraph.githubassets.com/7a27fd9e92b89417366065e831fb874681f86df4e914ac4e11a868de4d1549ea/InDieTasten/gh-sine-activity
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: gpg-signing-test
status: abandoned
@@ -885,8 +890,8 @@
language: null
created_at: "2024-07-22T12:20:39Z"
updated_at: "2024-07-22T13:11:31Z"
- og_image: >-
- https://opengraph.githubassets.com/25745dd2eeabfa3c3c279f7cc792a3972d13a04cb24ae038cbc8d062fd7533c3/InDieTasten/gpg-signing-test
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: lua-test
status: abandoned
@@ -898,8 +903,8 @@
language: Lua
created_at: "2024-07-22T20:48:06Z"
updated_at: "2024-07-22T21:15:22Z"
- og_image: >-
- https://opengraph.githubassets.com/fe5cbf7bca9d6a2c219e6137fe0a50835ffc9d9cf02c1a1eb146542dd70d9f7a/InDieTasten/lua-test
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
- name: Anti-GH-Pages-Yank
status: abandoned
@@ -911,6 +916,6 @@
language: null
created_at: "2024-10-20T21:25:42Z"
updated_at: "2024-11-08T04:00:20Z"
- og_image: >-
- https://opengraph.githubassets.com/7257ee479f942d8005045c2948a60a6e0455b2ce88463571d098c4e88d68e04c/InDieTasten/Anti-GH-Pages-Yank
+ og_image: /project-placeholder.svg
+ custom_image_url: null
archived: false
diff --git a/package.json b/package.json
index 89c314c..be94bbb 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,8 @@
"private": true,
"scripts": {
"dev": "next dev --turbopack",
- "build": "next build"
+ "download-project-images": "node scripts/download-project-images.js",
+ "build": "npm run download-project-images && next build"
},
"dependencies": {
"@headlessui/react": "2.2.4",
diff --git a/public/project-placeholder.svg b/public/project-placeholder.svg
new file mode 100644
index 0000000..e287382
--- /dev/null
+++ b/public/project-placeholder.svg
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/scripts/download-project-images.js b/scripts/download-project-images.js
new file mode 100644
index 0000000..3309842
--- /dev/null
+++ b/scripts/download-project-images.js
@@ -0,0 +1,141 @@
+#!/usr/bin/env node
+
+/**
+ * Download Project Images Script
+ * Downloads custom repository images for projects during build time
+ */
+
+const fs = require('fs');
+const path = require('path');
+const yaml = require('js-yaml');
+
+const log = (message) => console.log(`[INFO] ${message}`);
+const warn = (message) => console.log(`[WARN] ${message}`);
+const error = (message) => {
+ console.error(`[ERROR] ${message}`);
+ process.exit(1);
+};
+
+async function downloadProjectImages() {
+ const projectsFile = path.join(process.cwd(), '_data', 'projects.yml');
+ const imagesDir = path.join(process.cwd(), 'public', 'project-images');
+
+ // Check if projects file exists
+ if (!fs.existsSync(projectsFile)) {
+ error('projects.yml not found. Please run fetch-projects.sh first.');
+ }
+
+ // Load projects data
+ log('Loading projects data...');
+ const fileContents = fs.readFileSync(projectsFile, 'utf8');
+ const projects = yaml.load(fileContents);
+
+ // Create images directory
+ if (!fs.existsSync(imagesDir)) {
+ fs.mkdirSync(imagesDir, { recursive: true });
+ log('Created project-images directory');
+ }
+
+ // Download images for projects with custom_image_url
+ const projectsWithImages = projects.filter(project => project.custom_image_url);
+
+ if (projectsWithImages.length === 0) {
+ log('No projects with custom images found');
+ return;
+ }
+
+ log(`Found ${projectsWithImages.length} projects with custom images`);
+
+ for (const project of projectsWithImages) {
+ try {
+ const imageUrl = project.custom_image_url;
+ const imagePath = project.og_image; // This is the local path like "/project-images/filename"
+
+ if (!imagePath || !imagePath.startsWith('/project-images/')) {
+ warn(`Skipping ${project.name}: Invalid image path`);
+ continue;
+ }
+
+ // Extract filename from the path
+ const filename = path.basename(imagePath);
+ const fullPath = path.join(imagesDir, filename);
+
+ // Skip if image already exists
+ if (fs.existsSync(fullPath)) {
+ log(`Image already exists for ${project.name}, skipping`);
+ continue;
+ }
+
+ log(`Downloading image for ${project.name}...`);
+
+ const response = await fetch(imageUrl);
+ if (!response.ok) {
+ warn(`Failed to download image for ${project.name}: ${response.status} ${response.statusText}`);
+ continue;
+ }
+
+ const arrayBuffer = await response.arrayBuffer();
+ const buffer = Buffer.from(arrayBuffer);
+
+ // Determine correct file extension based on content type or magic bytes
+ let extension = '.jpg'; // default
+ const contentType = response.headers.get('content-type');
+ if (contentType) {
+ if (contentType.includes('png')) {
+ extension = '.png';
+ } else if (contentType.includes('gif')) {
+ extension = '.gif';
+ } else if (contentType.includes('webp')) {
+ extension = '.webp';
+ }
+ } else {
+ // Check magic bytes for GIF
+ if (buffer.length >= 3 &&
+ buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) {
+ extension = '.gif';
+ } else if (buffer.length >= 8 &&
+ buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4E && buffer[3] === 0x47) {
+ extension = '.png';
+ }
+ }
+
+ // Update filename with correct extension
+ const baseFilename = filename.replace(/\.[^.]+$/, ''); // Remove existing extension
+ const correctedFilename = baseFilename + extension;
+ const correctedPath = path.join(imagesDir, correctedFilename);
+
+ fs.writeFileSync(correctedPath, buffer);
+ log(`Downloaded and saved: ${correctedFilename}`);
+
+ // If the filename changed, we need to update the project data
+ if (correctedFilename !== filename) {
+ log(`Updated filename for ${project.name}: ${filename} -> ${correctedFilename}`);
+ project.og_image = `/project-images/${correctedFilename}`;
+ }
+
+ } catch (err) {
+ warn(`Error downloading image for ${project.name}: ${err.message}`);
+ }
+ }
+
+ // Update the projects file with any corrected filenames
+ const updatedYaml = yaml.dump(projects, {
+ indent: 2,
+ lineWidth: 120,
+ quotingType: '"',
+ forceQuotes: false
+ });
+
+ fs.writeFileSync(projectsFile, updatedYaml);
+ log('Project images download completed');
+}
+
+// Run the script
+if (require.main === module) {
+ downloadProjectImages().catch(error => {
+ console.error('Failed to download project images:', error);
+ process.exit(1);
+ });
+}
+
+module.exports = { downloadProjectImages };
\ No newline at end of file
diff --git a/scripts/fetch-projects.sh b/scripts/fetch-projects.sh
index 4be70c5..d82f523 100755
--- a/scripts/fetch-projects.sh
+++ b/scripts/fetch-projects.sh
@@ -130,10 +130,11 @@ async function fetchAllRepos() {
tags.unshift(repo.language);
}
- // Try to get repository's actual OpenGraph image first
+ // Try to get repository's custom social preview image
let ogImage = null;
+ let customImageUrl = null;
- // Try to extract OpenGraph image from repository HTML page
+ // Try to extract custom repository image from repository HTML page
try {
const repoPageResponse = await fetch(repo.html_url, {
method: 'GET',
@@ -148,16 +149,30 @@ async function fetchAllRepos() {
// Extract OpenGraph image from meta tag
const ogImageMatch = htmlContent.match(/]*>/i);
if (ogImageMatch && ogImageMatch[1]) {
- ogImage = ogImageMatch[1];
+ const imageUrl = ogImageMatch[1];
+
+ // Check if this is a custom repository image (not the dynamic OpenGraph one)
+ if (imageUrl.includes('repository-images.githubusercontent.com')) {
+ // This is a custom social preview image - store URL for build-time processing
+ customImageUrl = imageUrl;
+
+ // Extract a unique identifier from the URL to create local filename
+ const urlParts = imageUrl.split('/');
+ const imageId = urlParts[urlParts.length - 1] || 'unknown';
+ const localFileName = `${repo.name}-${imageId}`;
+ ogImage = `/project-images/${localFileName}`;
+
+ console.log(`Custom image found for ${repo.name}: ${imageUrl}`);
+ }
}
}
} catch (error) {
console.log(`Could not fetch repository page for ${repo.full_name}: ${error.message}`);
}
- // Use the dynamic OpenGraph image as fallback
+ // Use generic placeholder for projects without custom images
if (!ogImage) {
- ogImage = `https://opengraph.githubassets.com/1/${repo.full_name}`;
+ ogImage = '/project-placeholder.svg';
}
const project = {
@@ -171,6 +186,7 @@ async function fetchAllRepos() {
created_at: repo.created_at,
updated_at: repo.updated_at,
og_image: ogImage,
+ custom_image_url: customImageUrl,
archived: repo.archived
};
diff --git a/src/interfaces/project.ts b/src/interfaces/project.ts
index f7f0db2..3c7d25c 100644
--- a/src/interfaces/project.ts
+++ b/src/interfaces/project.ts
@@ -10,5 +10,6 @@ export type Project = {
created_at?: string;
updated_at?: string;
og_image?: string;
+ custom_image_url?: string;
archived?: boolean;
};
\ No newline at end of file