diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..4e84df5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: true
+contact_links:
+ - name: Find collaborators or share an idea
+ url: https://github.com/orgs/THD-AI/discussions/categories/ideas
+ about: Meet people before you have a finished project.
+ - name: Membership or private questions
+ url: https://github.com/THD-AI/.github/blob/main/SUPPORT.md
+ about: Find the right contact without publishing personal information.
diff --git a/.github/ISSUE_TEMPLATE/project-submission.yml b/.github/ISSUE_TEMPLATE/project-submission.yml
new file mode 100644
index 0000000..198d12c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/project-submission.yml
@@ -0,0 +1,72 @@
+name: Share a project
+description: Add a personal or university project to the THD AI showroom.
+title: "[Showroom] "
+labels: [showcase-submission]
+body:
+ - type: markdown
+ attributes:
+ value: "Share work you care about. Your repository can stay where it is. A maintainer reviews submissions before they appear in the showroom. Do not include credentials, private coursework, or personal documents."
+ - type: input
+ id: name
+ attributes:
+ label: Project name
+ validations:
+ required: true
+ - type: input
+ id: repository
+ attributes:
+ label: Public GitHub repository
+ placeholder: https://github.com/your-name/your-project
+ validations:
+ required: true
+ - type: textarea
+ id: purpose
+ attributes:
+ label: What does it do, and why did you make it?
+ description: A few clear sentences are enough. Include the current status and limitations.
+ validations:
+ required: true
+ - type: textarea
+ id: creators
+ attributes:
+ label: Creators and credits
+ description: Name the people who worked on it, link their public profiles, and credit material you used.
+ validations:
+ required: true
+ - type: input
+ id: demo
+ attributes:
+ label: Demo, screenshots, or setup instructions
+ description: Give someone a way to understand or try the project.
+ validations:
+ required: true
+ - type: dropdown
+ id: category
+ attributes:
+ label: Where does it fit?
+ multiple: true
+ options:
+ - Campus life
+ - AI & ML
+ - Learning
+ validations:
+ required: true
+ - type: textarea
+ id: collaboration
+ attributes:
+ label: What kind of company are you looking for?
+ description: Feedback, contributors, a new maintainer, or simply sharing something you enjoyed making.
+ - type: textarea
+ id: context
+ attributes:
+ label: A little context
+ description: How does the work connect to THD? You may also explain your process or tools, including AI assistance, if useful. No proof of enrolment or private documents are needed.
+ - type: checkboxes
+ id: permission
+ attributes:
+ label: Permission to share
+ options:
+ - label: I created this project or have its creators' permission to submit it, and the image/description can be displayed with credit.
+ required: true
+ - label: I have not included credentials, private student information, or restricted university material.
+ required: true
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..2a65585
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,10 @@
+## What changes?
+
+For a project submission, link its issue and explain the listing.
+
+## Checks
+
+- Project title, description, creators, status, and links are accurate.
+- Image permission and source credit are recorded in `asset-sources.json` and the credits page when applicable.
+- No credentials, private coursework, or private personal information are included.
+- `node scripts/check.mjs` passes, and visible changes were checked in a browser.
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
new file mode 100644
index 0000000..b35425d
--- /dev/null
+++ b/.github/workflows/pages.yml
@@ -0,0 +1,43 @@
+name: Check and publish showroom
+on:
+ push:
+ branches: [main]
+ pull_request:
+ workflow_dispatch:
+permissions:
+ contents: read
+ pages: read
+concurrency:
+ group: showroom-${{ github.ref }}
+ cancel-in-progress: true
+jobs:
+ check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+ - name: Validate project data and public site
+ run: node scripts/check.mjs
+ - name: Check browser JavaScript syntax
+ run: node --check site/app.js
+ - name: Configure GitHub Pages
+ if: github.event_name != 'pull_request'
+ uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
+ - name: Package only the public website
+ if: github.event_name != 'pull_request'
+ uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
+ with:
+ path: site
+ publish:
+ if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
+ needs: check
+ runs-on: ubuntu-latest
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Publish the checked website
+ id: deployment
+ uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
diff --git a/.github/workflows/submissions.yml b/.github/workflows/submissions.yml
new file mode 100644
index 0000000..fa09c57
--- /dev/null
+++ b/.github/workflows/submissions.yml
@@ -0,0 +1,33 @@
+name: Welcome a project submission
+on:
+ issues:
+ types: [opened]
+permissions:
+ contents: read
+ issues: write
+jobs:
+ welcome:
+ if: contains(github.event.issue.labels.*.name, 'showcase-submission')
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const body = context.payload.issue.body || '';
+ const match = body.match(/https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)/);
+ let repositoryNote = 'Please include a public GitHub repository so people can explore the work.';
+ if (match) {
+ try {
+ const { data } = await github.rest.repos.get({ owner: match[1], repo: match[2] });
+ repositoryNote = data.private
+ ? 'The repository is private. Please share a public project link; private material is not published here.'
+ : `The linked repository is publicly readable: **${data.full_name}**.`;
+ } catch {
+ repositoryNote = 'The linked repository could not be read publicly. Please check its URL and visibility.';
+ }
+ }
+ await github.rest.issues.createComment({
+ ...context.repo,
+ issue_number: context.issue.number,
+ body: `Thanks for sharing your work with THD AI.\n\n${repositoryNote}\n\nA maintainer will review the description, creator credits, image permissions, project status, and a way to try it. Acceptance is recorded in a pull request updating the showroom catalogue. This reply does not publish or approve the project.\n\nYour repository can stay in your own account. In the meantime, you can meet collaborators in [Discussions](https://github.com/orgs/THD-AI/discussions/categories/ideas).`
+ });
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..a9e4082
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,27 @@
+# Help shape the showroom
+
+THD AI is an independent place for people connected to Deggendorf Institute of Technology to share work, find inspiration, and collaborate in their own time. Personal work and university projects belong alongside one another. AI-assisted work is welcome; quality, care, accurate credit, and clear limitations matter.
+
+## Share a project
+
+Use the [project submission form](https://github.com/THD-AI/thd-ai.github.io/issues/new?template=project-submission.yml). Include a public repository, its creators, a clear description, a way to try or understand it, and what kind of collaboration you want. You can keep the repository in your own account. Organization membership is not required to submit or contribute.
+
+The submission workflow leaves an acknowledgement and checks whether the repository is publicly readable. It does not approve or automatically publish a listing. A maintainer reviews the information and image permission, then opens a pull request to update `site/projects.json`. Keep source attribution in `asset-sources.json` and `site/credits/index.html`. Close the submission issue when the listing is published. An experimental project is welcome when it is described honestly and has a useful way in.
+
+To update or remove your own listing, open an issue or pull request. If you need to discuss something privately, [email Alexander](mailto:alex.rudaev@outlook.com).
+
+## Change the website
+
+The website is buildless HTML, CSS, and JavaScript in `site/`. It uses a local JSON catalogue, with no visitor accounts or database. Search and filters run in the browser. An external Google Fonts stylesheet provides the fonts; system sans-serif fonts are the fallback.
+
+1. Fork the repository or create a branch if you have write access.
+2. Edit a focused piece of the site, add a credited project, or improve the documentation.
+3. Run `node scripts/check.mjs` and `node --check site/app.js`.
+4. Serve `site/` locally, for example with `python -m http.server 4173 --directory site`, and check the relevant desktop/mobile flow.
+5. Open a pull request. The same validation runs in GitHub Actions. A checked merge to `main` publishes only `site/` to GitHub Pages.
+
+Do not put private archive files or credentials in `site/`. Use text content for project metadata, not injected HTML. Keep the approved campus banner and existing project branding intact unless a change is agreed with its creators.
+
+## Work together
+
+Members can create and lead public and private repositories. Use [Discussions](https://github.com/orgs/THD-AI/discussions) to meet people, share a work in progress, or ask a question. Follow the [community conduct guidance](https://github.com/THD-AI/.github/blob/main/CODE_OF_CONDUCT.md), give people credit, and respect each project's licence and decisions.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cb196eb
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+MIT License
+
+Copyright (c) 2026 Alexander Rudaev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+This licence covers the website's original code and documentation. Images,
+archived artwork, project banners, and the projects themselves retain their
+separate licences and credits in site/credits/index.html.
diff --git a/asset-sources.json b/asset-sources.json
new file mode 100644
index 0000000..ff53457
--- /dev/null
+++ b/asset-sources.json
@@ -0,0 +1,7 @@
+{
+ "project-chexvision.png": "https://raw.githubusercontent.com/arudaev/chexvision/main/.github/banner.png",
+ "project-painfully-trivial.png": "https://raw.githubusercontent.com/arudaev/Painfully-Trivial/main/.github/banner.png",
+ "project-devablos.png": "https://raw.githubusercontent.com/THD-AI/Devablos-Project-V2/main/.github/banner.png",
+ "project-room.webp": "https://raw.githubusercontent.com/arudaev/THD-Room-Finder/main/website/assets/hero-day.webp",
+ "history-avatar-first-redesign.png": "https://github.com/THD-AI/.github/tree/main/archive/2026-10-17-first-redesign"
+}
diff --git a/scripts/check.mjs b/scripts/check.mjs
new file mode 100644
index 0000000..601e927
--- /dev/null
+++ b/scripts/check.mjs
@@ -0,0 +1,74 @@
+import { readFile, access, readdir, stat } from 'node:fs/promises';
+import { fileURLToPath } from 'node:url';
+import path from 'node:path';
+import assert from 'node:assert/strict';
+
+const root = fileURLToPath(new URL('../', import.meta.url));
+const site = path.join(root, 'site');
+const collection = JSON.parse(await readFile(path.join(site, 'projects.json'), 'utf8'));
+const categories = new Set(['campus', 'ai', 'learning']);
+const ids = new Set();
+assert(Array.isArray(collection.projects) && collection.projects.length > 0, 'Provide a non-empty project collection');
+function https(url, field) {
+ const parsed = new URL(url);
+ assert.equal(parsed.protocol, 'https:', `${field} must use HTTPS`);
+ assert(!parsed.username && !parsed.password, `${field} must not contain credentials`);
+ return parsed;
+}
+for (const project of collection.projects) {
+ assert(/^[a-z0-9-]+$/.test(project.id) && !ids.has(project.id), `Unique safe project ID: ${project.id}`);
+ ids.add(project.id);
+ for (const key of ['title', 'description', 'categoryLabel', 'status', 'collaboration']) {
+ assert(typeof project[key] === 'string' && project[key].trim(), `${project.id}: missing ${key}`);
+ }
+ assert(project.categories?.length && project.categories.every(c => categories.has(c)), `${project.id}: unknown category`);
+ const repository = https(project.repository, 'repository');
+ assert.equal(repository.hostname, 'github.com', 'Project repository must be a public GitHub link');
+ assert(/^\/[^/]+\/[^/]+\/?$/.test(repository.pathname), 'Use a repository URL, not a subpage');
+ if (project.community) assert.equal(https(project.community, 'community').hostname, 'github.com');
+ if (project.demo) https(project.demo, 'demo');
+ assert(Array.isArray(project.creators) && project.creators.length > 0, `${project.id}: credit the creators`);
+ for (const creator of project.creators) {
+ assert(creator.name?.trim(), 'Creator name required');
+ https(creator.url, 'creator');
+ }
+ assert(Array.isArray(project.tags) && project.tags.every(t => typeof t === 'string'), 'Project tags must be strings');
+ if (project.image) {
+ assert(project.image.startsWith('assets/') && !project.image.includes('..'), 'Project image must be a local asset');
+ await access(path.join(site, project.image));
+ assert(project.imageAlt?.trim(), 'Project image needs alt text');
+ }
+}
+async function walk(folder) {
+ const files = [];
+ for (const entry of await readdir(folder)) {
+ const full = path.join(folder, entry);
+ if ((await stat(full)).isDirectory()) files.push(...await walk(full));
+ else files.push(full);
+ }
+ return files;
+}
+const files = await walk(site);
+for (const file of files.filter(f => f.endsWith('.html'))) {
+ const html = await readFile(file, 'utf8');
+ assert(//.test(html), 'Document language required');
+ assert(/[^<]+<\/title>/.test(html), 'Document title required');
+ for (const match of html.matchAll(/(?:src|href)="([^"]+)"/g)) {
+ const target = match[1];
+ if (/^(https:|mailto:)/.test(target)) continue;
+ if (target.startsWith('#')) {
+ assert(html.includes(`id="${target.slice(1)}"`), `${file}: missing section ${target}`);
+ continue;
+ }
+ const targetPath = target.split(/[?#]/)[0];
+ const destination = targetPath.startsWith('/') ? path.resolve(site, '.' + targetPath) : path.resolve(path.dirname(file), targetPath);
+ assert(destination === site || destination.startsWith(site + path.sep), `${file}: path escapes site`);
+ await access(destination);
+ if ((await stat(destination)).isDirectory()) await access(path.join(destination, 'index.html'));
+ }
+ for (const image of html.matchAll(/]*>/g)) assert(/\balt="[^"]*"/.test(image[0]), 'Every image needs alt text');
+}
+assert(files.every(f => !/\.git|\.env|\.pem|\.key$/.test(path.relative(site, f))), 'Only public website files may be deployed');
+assert(!files.some(f => /member-profile|\.github-private|first-redesign\/profile/.test(f)), 'Private archive content must not ship with the website');
+console.log(`Validated ${ids.size} credited projects, ${files.filter(f => f.endsWith('.html')).length} pages, local links, image alternatives, HTTPS URLs, and deployment boundaries.`);
diff --git a/site/.nojekyll b/site/.nojekyll
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/site/.nojekyll
@@ -0,0 +1 @@
+
diff --git a/site/404.html b/site/404.html
new file mode 100644
index 0000000..38efbce
--- /dev/null
+++ b/site/404.html
@@ -0,0 +1,6 @@
+
+
That page may have moved. The showroom and project collection are still here.
Back to the showroom ↗
+
diff --git a/site/app.js b/site/app.js
new file mode 100644
index 0000000..0d75cb4
--- /dev/null
+++ b/site/app.js
@@ -0,0 +1,103 @@
+const grid = document.querySelector('#project-grid');
+const search = document.querySelector('#project-search');
+const filters = [...document.querySelectorAll('[data-filter]')];
+let projects = [];
+let activeFilter = 'all';
+
+function element(tag, className, text) {
+ const node = document.createElement(tag);
+ if (className) node.className = className;
+ if (text !== undefined) node.textContent = text;
+ return node;
+}
+function safeLink(text, url, className = '') {
+ const link = element('a', className, text);
+ const parsed = new URL(url);
+ if (parsed.protocol !== 'https:') throw new Error('Unsupported project link');
+ link.href = parsed.href;
+ link.target = '_blank';
+ link.rel = 'noopener noreferrer';
+ return link;
+}
+function projectArt(project) {
+ const art = element('div', `project-art art-${project.id}`);
+ if (project.image) {
+ const img = element('img');
+ img.src = project.image;
+ img.alt = project.imageAlt;
+ img.loading = 'lazy';
+ img.width = 660;
+ img.height = 400;
+ art.append(img);
+ } else if (project.id === 'studybooks') {
+ art.setAttribute('aria-hidden', 'true');
+ const books = element('div', 'book-art');
+ for (const title of ['Learn it.', 'Keep it.']) {
+ const sheet = element('div', 'book-sheet');
+ sheet.append(element('small', '', 'Course notes'), element('strong', '', title));
+ for (let i = 0; i < 4; i++) sheet.append(element('span'));
+ books.append(sheet);
+ }
+ art.append(books);
+ } else {
+ art.setAttribute('aria-hidden', 'true');
+ const network = element('div', 'network-art');
+ for (const size of [3, 4, 2]) {
+ const column = element('div', 'node-column');
+ for (let i = 0; i < size; i++) column.append(element('span', 'network-node'));
+ network.append(column);
+ }
+ art.append(network);
+ }
+ return art;
+}
+function card(project) {
+ const article = element('article', 'project-card');
+ const meta = element('div', 'card-meta');
+ meta.append(element('span', '', project.categoryLabel), element('span', 'card-status', project.status));
+ const creators = element('p', 'project-creators');
+ creators.append(document.createTextNode('By '));
+ project.creators.forEach((creator, index) => {
+ if (index) creators.append(document.createTextNode(', '));
+ creators.append(safeLink(creator.name, creator.url));
+ });
+ const bottom = element('div', 'card-bottom');
+ bottom.append(safeLink('Explore the project ↗', project.repository));
+ if (project.demo) bottom.append(safeLink(project.demoLabel || 'Try it ↗', project.demo));
+ else if (project.community) bottom.append(safeLink('Build with others ↗', project.community));
+ article.append(projectArt(project), meta, element('h3', '', project.title), element('p', 'project-description', project.description), creators, bottom, element('p', 'collaboration', project.collaboration));
+ return article;
+}
+function render() {
+ const query = search.value.trim().toLocaleLowerCase();
+ const visible = projects.filter(project => {
+ const haystack = [project.title, project.description, project.categoryLabel, ...project.tags, ...project.creators.map(c => c.name)].join(' ').toLocaleLowerCase();
+ return (activeFilter === 'all' || project.categories.includes(activeFilter)) && haystack.includes(query);
+ });
+ grid.replaceChildren(...visible.map(card));
+ document.querySelector('#project-count').textContent = `${visible.length} ${visible.length === 1 ? 'project' : 'projects'} to explore`;
+ document.querySelector('#empty-state').hidden = visible.length !== 0;
+ for (const button of filters) button.setAttribute('aria-pressed', String(button.dataset.filter === activeFilter));
+}
+async function loadProjects() {
+ document.querySelector('#load-error').hidden = true;
+ document.querySelector('#project-count').textContent = 'Opening the showroom…';
+ try {
+ const response = await fetch(new URL('projects.json', import.meta.url));
+ if (!response.ok) throw new Error(`Project data: ${response.status}`);
+ const data = await response.json();
+ if (!Array.isArray(data.projects)) throw new Error('Invalid project collection');
+ projects = data.projects;
+ render();
+ } catch {
+ grid.replaceChildren();
+ document.querySelector('#empty-state').hidden = true;
+ document.querySelector('#load-error').hidden = false;
+ document.querySelector('#project-count').textContent = 'Project catalogue unavailable';
+ }
+}
+for (const button of filters) button.addEventListener('click', () => { activeFilter = button.dataset.filter; render(); });
+search.addEventListener('input', render);
+document.querySelector('#reset-search').addEventListener('click', () => { search.value = ''; activeFilter = 'all'; render(); search.focus(); });
+document.querySelector('#retry-load').addEventListener('click', loadProjects);
+loadProjects();
diff --git a/site/assets/campus.png b/site/assets/campus.png
new file mode 100644
index 0000000..6cb8862
Binary files /dev/null and b/site/assets/campus.png differ
diff --git a/site/assets/history-avatar-first-redesign.png b/site/assets/history-avatar-first-redesign.png
new file mode 100644
index 0000000..4b04953
Binary files /dev/null and b/site/assets/history-avatar-first-redesign.png differ
diff --git a/site/assets/history-community-hub.png b/site/assets/history-community-hub.png
new file mode 100644
index 0000000..0bf3381
Binary files /dev/null and b/site/assets/history-community-hub.png differ
diff --git a/site/assets/history-organization-avatar.png b/site/assets/history-organization-avatar.png
new file mode 100644
index 0000000..3dd85c6
Binary files /dev/null and b/site/assets/history-organization-avatar.png differ
diff --git a/site/assets/history-public-profile.png b/site/assets/history-public-profile.png
new file mode 100644
index 0000000..2b4f0a3
Binary files /dev/null and b/site/assets/history-public-profile.png differ
diff --git a/site/assets/history-schedule-2024.jpg b/site/assets/history-schedule-2024.jpg
new file mode 100644
index 0000000..10ce1bc
Binary files /dev/null and b/site/assets/history-schedule-2024.jpg differ
diff --git a/site/assets/member-workspace.png b/site/assets/member-workspace.png
new file mode 100644
index 0000000..4873b5e
Binary files /dev/null and b/site/assets/member-workspace.png differ
diff --git a/site/assets/project-chexvision.png b/site/assets/project-chexvision.png
new file mode 100644
index 0000000..a7c6c51
Binary files /dev/null and b/site/assets/project-chexvision.png differ
diff --git a/site/assets/project-devablos.png b/site/assets/project-devablos.png
new file mode 100644
index 0000000..d4f9132
Binary files /dev/null and b/site/assets/project-devablos.png differ
diff --git a/site/assets/project-painfully-trivial.png b/site/assets/project-painfully-trivial.png
new file mode 100644
index 0000000..653e211
Binary files /dev/null and b/site/assets/project-painfully-trivial.png differ
diff --git a/site/assets/project-room.webp b/site/assets/project-room.webp
new file mode 100644
index 0000000..72c40a8
Binary files /dev/null and b/site/assets/project-room.webp differ
diff --git a/site/assets/showcase.png b/site/assets/showcase.png
new file mode 100644
index 0000000..b41402b
Binary files /dev/null and b/site/assets/showcase.png differ
diff --git a/site/assets/symbol.png b/site/assets/symbol.png
new file mode 100644
index 0000000..eec5327
Binary files /dev/null and b/site/assets/symbol.png differ
diff --git a/site/credits/index.html b/site/credits/index.html
new file mode 100644
index 0000000..34c4bd8
--- /dev/null
+++ b/site/credits/index.html
@@ -0,0 +1,10 @@
+
+Credits & licences – THD AI
+Skip to content
+THD AIShare your project ↗
+
The people and sources behind the work
Give good work its credit.
The showroom connects projects. It does not take ownership of them. Every project keeps its original creators, repository, and licence.
+
Campus artwork
The public campus banner adapts the campus illustration from THD Room Finder. Its source photograph is Hauptcampus der Technischen Hochschule Deggendorf 2018 by Danube-Waidler, licensed CC BY-SA 4.0. The repainted illustration and public banner are shared under that same licence. The banner widens the composition, changes the light and palette, simplifies people into grey silhouettes, and adds THD AI lettering.
The member-workspace and showcase illustrations follow the same visual family, using the Room Finder art as visual references. These new illustrations are also shared under CC BY-SA 4.0. The original public banner remains unchanged. Detailed source notes and production briefs live in the brand record.
+
The open-portal symbol
The THD AI symbol is an open architectural portal with a warm sun at its centre: room for a person, an idea, and what comes next. It replaces the earlier boxed initials. The new symbol and earlier typographic badge are community identity artwork shared under CC BY 4.0. No official university marks are used.
+
Project images and creators
THD Room Finder: Alexander Rudaev. The study-space illustration is taken from its original public repository; the project is GPL-3.0. Consult the project's artwork notes for asset-specific terms.
CheXVision: Alexander Rudaev. Original project banner from the source repository; its licence is retained.
Painfully Trivial: Alexander Rudaev, FaresM7, and TheSameerCode, as credited contributors. Original project banner from the source repository; its licence is retained.
Devablos V2: Alexander Rudaev and Denisa Iulia. Original banner from the archived project; GPL-3.0.
Course Studybooks and CheXVision Mini: Alexander Rudaev. The simple book and network motifs on this website are presentation graphics, rather than screenshots of either project.
Image source URLs are recorded in asset-sources.json. Historical visuals retain their original rights and are not relicensed by appearing in the archive.
+
Website
The original website code and documentation are MIT-licensed. This does not relicense the images or linked projects. The typefaces are Manrope and DM Sans, served by Google Fonts, with system sans-serif fallbacks. The site has no analytics, visitor accounts, or advertising.
+
diff --git a/site/history/index.html b/site/history/index.html
new file mode 100644
index 0000000..c7084ad
--- /dev/null
+++ b/site/history/index.html
@@ -0,0 +1,6 @@
+
+Visual history – THD AI
+Skip to content
+THD AIShare your project ↗
+
Earlier chapters / Visual history
A little history. Room for what comes next.
THD AI began as a 2023 cohort experiment. The direction has changed, but the work that came before it still belongs in the story. These are preserved originals and the illustrations that followed.
2023 / Original public profile
The first cohort profile imagined students reaching toward what they could make together. The original image is preserved unchanged.
2023 / Community hub
The early discussion hub paired collaborative learning with a painterly illustration.
2024 / A new semester
A campus scene from the winter-semester calendar announcement. The schedule itself remains a dated historical record.
2023 / First organization avatar
The organization’s original avatar, saved before the identity changed.
2026 / The first redesign
The initial THD AI typographic badge. Replaced by the open-portal symbol, and kept here as part of the process.
2026 / An open campus
The approved public banner. Its soft campus light, lavender haze, and grey silhouettes set the direction for the current identity.
2026 / Room for ideas
The member-workspace banner brings the same atmosphere indoors, around personal projects, collaboration, and coffee.
2026 / A shared showroom
A new scene for showing work, asking questions, and finding people who care about the same things.
The member-only chapter is preserved too.
The original internal illustration and member documents stay in the private member archive. The first member-profile redesign is saved there as well. They have not been copied into this public page.
+
diff --git a/site/index.html b/site/index.html
new file mode 100644
index 0000000..e1d337e
--- /dev/null
+++ b/site/index.html
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+ THD AI – a place for the work you care about
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Skip to projects
+
+ THD AI
+
+ Share your project ↗
+
+
+
+
+
Independent projects · Deggendorf & beyond
+
A place for the work you care about.
+
Personal projects. University experiments. Useful things. A shared space for people around THD who love making them.
The first projects get the shelf started. Yours can be next. A project can live in your own repository.
+
+
+
+
02 / Why this exists
Good work deserves a little company.
Some projects start in a lecture. Some start with a small annoyance, a late-night idea, or a question you can't leave alone. They deserve a life beyond a submission folder.
THD AI is an independent place to show that work and meet people who care about it. For anyone connected to Deggendorf Institute of Technology with curiosity and a love for AI, software, and making things.
You choose your own project, pace, and collaborators. AI-assisted or made without AI, the important things are care, quality, honest credit, and something worth sharing.
It doesn't need to be perfect. Give it a clear description, show what works, credit the people and sources behind it, and tell us what kind of company you're looking for.
Keep your repository where it is. A reviewed submission adds a link here. Organization members can also create and lead repositories of their own.
01
Show us the idea.
Link a public repository, explain what it does, and name its creators.
02
Make it easy to explore.
A screenshot, a demo, or clear setup instructions gives someone a way in.
03
Say what comes next.
Feedback? A collaborator? Someone to take it further? Tell people.
+
+
☕
Some ideas are better over coffee.
Want to join, collaborate, or connect work happening around THD? Email Alexander Rudaev. He reads it daily, weekends included. A cup of coffee is a remarkably good opening argument.