Skip to content

Score example hub #179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
32a8a8f
Squashed 'gen/external-repos/score-spec/examples/' content from commi…
TobiasBabin May 16, 2025
7459bac
Merge commit '32a8a8f1658b7cb678128ed19e4904810325dc12' as 'gen/exter…
TobiasBabin May 16, 2025
f4c526f
Added first pull of external repo and README addition on external con…
TobiasBabin May 16, 2025
2330575
Exclude gen dir from dprint check
TobiasBabin May 16, 2025
3541e69
Squashed 'gen/external-repos/score-spec/community-provisioners/' cont…
TobiasBabin May 16, 2025
6f06bbc
Merge commit '3541e6955c881faffddc613515fdb1b4f0a3730e' as 'gen/exter…
TobiasBabin May 16, 2025
2b49323
Integrating external content
TobiasBabin May 16, 2025
db83b59
Added yarn commands and documentation to update external content
TobiasBabin May 16, 2025
ac14c31
gen examples script setup
sberoch Jun 7, 2025
74d9a60
added navbar navigation and examples initial content
sberoch Jun 7, 2025
2805190
examples index page
sberoch Jun 7, 2025
d903924
gen script builds category pages
sberoch Jun 7, 2025
ea28cd0
gen script builds files with frontmatter
sberoch Jun 7, 2025
94863e2
gen script builds files with example file shortcodes
sberoch Jun 7, 2025
bb7d80f
fix example file generation. examples list page without filters
sberoch Jun 7, 2025
52672e0
filters for examples list pages
sberoch Jun 7, 2025
020709f
github urls. single page without tabs
sberoch Jun 8, 2025
cbfbd30
tabs in example pages
sberoch Jun 8, 2025
d923c5f
removed examplecount in single example pages
sberoch Jun 8, 2025
d80c28a
Merge branch 'main' into score-example-hub
mathieu-benoit Jun 9, 2025
99b825d
yarn fmt
mathieu-benoit Jun 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/public
resources/
/resources/
node_modules/
package-lock.json
.hugo_build.lock
Expand All @@ -10,3 +10,4 @@ package-lock.json
.score-compose/zz-default.provisioners.yaml
.score-compose/mounts/routing-QXKXWL/nginx.conf
compose.yaml
.prettierignore
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,38 @@ Example output.

To lint all documents, run `yarn lint`.

## Autogenerated content

### Score example hub

This site uses content from external Git repositories to create and continuously update the "Score example hub" pages. The external repos are pulled in to the `/gen/external-content` folder.

The commands for the initial integration of the repos are:

```bash
git remote add -f -t main --no-tags examples https://github.com/score-spec/examples.git
git remote add -f -t main --no-tags community-provisioners https://github.com/score-spec/community-provisioners.git
git read-tree --prefix=gen/external-content/score/specification -u examples/main:specification
git read-tree --prefix=gen/external-content/score/resources/default-provisioners -u examples/main:resources
git read-tree --prefix=gen/external-content/score/resources/community-provisioners -u community-provisioners/main
git add gen/external-content
git commit -s -S -m "Integrating external content"
```

These commands will not have to be repeated unless re-creating the repo integration, or moving the source location. In that case, remove the `remote`, delete the local contents, repeat these commands targeting the new location, and update the generation scripts.

### Pulling new content

To pull the current content from the remote example library repo, execute this command:

```bash
yarn gen-get-external-content
```

Refer to the [package.json`](./package.json) to see the actual implementation of this command.

## Exam

## Troubleshooting documentation site builds

This section covers common build issues with Hugo.
Expand Down
19 changes: 19 additions & 0 deletions assets/images/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 54 additions & 0 deletions assets/js/clipboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* eslint-disable quotes */
import Clipboard from "clipboard";

// Only run on /examples pages
if (window.location.pathname.startsWith("/examples")) {
var pre = document.getElementsByTagName("pre");

for (var i = 0; i < pre.length; ++i) {
var element = pre[i];
var mermaid = element.getElementsByClassName("language-mermaid")[0];

if (mermaid == null) {
element.insertAdjacentHTML(
"afterbegin",
`<button class="copy-btn">
<svg class="copy-icon" width='11' height='13' viewBox='0 0 11 13' fill='#000311' xmlns='http://www.w3.org/2000/svg'><path d='M3.55 10.5C3.20556 10.5 2.91667 10.3833 2.68333 10.15C2.45 9.91667 2.33333 9.63333 2.33333 9.3V1.71667C2.33333 1.37222 2.45 1.08333 2.68333 0.85C2.91667 0.616667 3.20556 0.5 3.55 0.5H9.11667C9.45 0.5 9.73622 0.619333 9.97533 0.858C10.214 1.09711 10.3333 1.38333 10.3333 1.71667V9.3C10.3333 9.63333 10.214 9.91667 9.97533 10.15C9.73622 10.3833 9.45 10.5 9.11667 10.5H3.55ZM3.55 9.5H9.11667C9.18333 9.5 9.23622 9.48044 9.27533 9.44133C9.314 9.40267 9.33333 9.35555 9.33333 9.3V1.71667C9.33333 1.65 9.314 1.59711 9.27533 1.558C9.23622 1.51933 9.18333 1.5 9.11667 1.5H3.55C3.48333 1.5 3.43067 1.51933 3.392 1.558C3.35289 1.59711 3.33333 1.65 3.33333 1.71667V9.3C3.33333 9.35555 3.35289 9.40267 3.392 9.44133C3.43067 9.48044 3.48333 9.5 3.55 9.5ZM1.2 12.8333C0.866667 12.8333 0.583333 12.7167 0.35 12.4833C0.116667 12.25 0 11.9667 0 11.6333V3.66667C0 3.53333 0.0471112 3.41667 0.141333 3.31667C0.236 3.21667 0.355555 3.16667 0.5 3.16667C0.633333 3.16667 0.75 3.21667 0.85 3.31667C0.95 3.41667 1 3.53333 1 3.66667V11.6333C1 11.6889 1.01933 11.736 1.058 11.7747C1.09711 11.8138 1.14444 11.8333 1.2 11.8333H7.16667C7.3 11.8333 7.41667 11.8833 7.51667 11.9833C7.61667 12.0833 7.66667 12.2 7.66667 12.3333C7.66667 12.4778 7.61667 12.5971 7.51667 12.6913C7.41667 12.786 7.3 12.8333 7.16667 12.8333H1.2Z' fill="#000311"/></svg>
<svg class="copy-icon-dark" width='11' height='13' viewBox='0 0 11 13' fill='white' xmlns='http://www.w3.org/2000/svg'><path d='M3.55 10.5C3.20556 10.5 2.91667 10.3833 2.68333 10.15C2.45 9.91667 2.33333 9.63333 2.33333 9.3V1.71667C2.33333 1.37222 2.45 1.08333 2.68333 0.85C2.91667 0.616667 3.20556 0.5 3.55 0.5H9.11667C9.45 0.5 9.73622 0.619333 9.97533 0.858C10.214 1.09711 10.3333 1.38333 10.3333 1.71667V9.3C10.3333 9.63333 10.214 9.91667 9.97533 10.15C9.73622 10.3833 9.45 10.5 9.11667 10.5H3.55ZM3.55 9.5H9.11667C9.18333 9.5 9.23622 9.48044 9.27533 9.44133C9.314 9.40267 9.33333 9.35555 9.33333 9.3V1.71667C9.33333 1.65 9.314 1.59711 9.27533 1.558C9.23622 1.51933 9.18333 1.5 9.11667 1.5H3.55C3.48333 1.5 3.43067 1.51933 3.392 1.558C3.35289 1.59711 3.33333 1.65 3.33333 1.71667V9.3C3.33333 9.35555 3.35289 9.40267 3.392 9.44133C3.43067 9.48044 3.48333 9.5 3.55 9.5ZM1.2 12.8333C0.866667 12.8333 0.583333 12.7167 0.35 12.4833C0.116667 12.25 0 11.9667 0 11.6333V3.66667C0 3.53333 0.0471112 3.41667 0.141333 3.31667C0.236 3.21667 0.355555 3.16667 0.5 3.16667C0.633333 3.16667 0.75 3.21667 0.85 3.31667C0.95 3.41667 1 3.53333 1 3.66667V11.6333C1 11.6889 1.01933 11.736 1.058 11.7747C1.09711 11.8138 1.14444 11.8333 1.2 11.8333H7.16667C7.3 11.8333 7.41667 11.8833 7.51667 11.9833C7.61667 12.0833 7.66667 12.2 7.66667 12.3333C7.66667 12.4778 7.61667 12.5971 7.51667 12.6913C7.41667 12.786 7.3 12.8333 7.16667 12.8333H1.2Z' fill="white"/></svg>
<svg class="copy-success-icon" width='11' height='13' viewBox='0 0 11 13' fill='#2156f6' xmlns='http://www.w3.org/2000/svg'><path d='M4.5 8.5L2 6L1.5 6.5L4.5 9.5L9.5 4.5L9 4L4.5 8.5Z' fill='#2156f6'/></svg>
<span class="copy-text">Copied!</span>
</button>`
);
}
}

var clipboard = new Clipboard(".copy-btn", {
target: function (trigger) {
return trigger.nextElementSibling;
},
});

clipboard.on("success", function (e) {
/*
console.info('Action:', e.action);
console.info('Text:', e.text);
console.info('Trigger:', e.trigger);
*/

e.clearSelection();

// Show success feedback
const button = e.trigger;
button.classList.add("copy-success");

// Remove success state after 2 seconds
setTimeout(() => {
button.classList.remove("copy-success");
}, 2000);
});

clipboard.on("error", function (e) {
console.error("Action:", e.action);
console.error("Trigger:", e.trigger);
});
}
131 changes: 131 additions & 0 deletions assets/js/multifilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
const examplesContainer = document.getElementById("examples-container");
const filters = document.querySelectorAll(".form-check-input");
const listItems = document.querySelectorAll(".examples-list-item");
const pageCount = document.getElementById("pagecount");
const urlParams = new URLSearchParams(window.location.search);

// Check inputs based on query parameters
filters.forEach((filter) => {
const type = filter.dataset.directory;
const value = filter.value;
if (urlParams.has(type)) {
const values = urlParams.get(type).split("+");
if (values.includes(value)) {
filter.checked = true;
}
}
});

filterCheckedItems();

filters.forEach((filter) => {
filter.addEventListener("change", () => {
let queryParams = filterCheckedItems();

let path = window.location.pathname;
if (window.location.pathname.endsWith("/")) {
path = window.location.pathname.slice(0, -1);
}
if (path !== "/" + examplesContainer.dataset.folder) {
path = "/" + examplesContainer.dataset.folder;
window.location.href = path + queryParams;
}
window.history.pushState(null, null, path + queryParams);
});
});

function filterCheckedItems() {
const checkedFilters = getCheckedFilters();
let queryParams = getQueryParams(checkedFilters);

filterItems(checkedFilters);
updatePageCount();
return queryParams;
}

function getCheckedFilters() {
return Array.from(filters)
.filter((filter) => filter.checked)
.map((filter) => ({
type: filter.dataset.directory,
value: filter.value,
}));
}

function updatePageCount() {
if (pageCount) {
const visibleItems = Array.from(listItems).filter(
(item) => item.style.display === "block"
);
pageCount.textContent = visibleItems.length;
}
}

function filterItems(checkedFilters) {
// Step 1: Separate filters by type
const filtersByType = checkedFilters.reduce((acc, filter) => {
if (!acc[filter.type]) {
acc[filter.type] = [];
}
acc[filter.type].push(filter.value);
return acc;
}, {});

// Step 2: Apply UNION logic
let unionResults = Array.from(listItems);
Object.entries(filtersByType).forEach(([type, values]) => {
unionResults = unionResults.filter((item) => {
if (item.dataset[type] && item.dataset[type].startsWith("[")) {
const array = item.dataset[type].slice(1, -1).split(" ");
return values.some((value) => array.includes(value));
}
return values.some((value) => item.dataset[type] === value);
});
});

// Step 3: Apply INTERSECTION logic
// For each item, check if it matches all filters of different types
const intersectionResults = unionResults.filter((item) => {
return Object.entries(filtersByType).every(([type, values]) => {
// If the item has a data attribute for this type, check if it matches any value
if (item.dataset[type]) {
if (item.dataset[type].startsWith("[")) {
const array = item.dataset[type].slice(1, -1).split(" ");
return values.some((value) => array.includes(value));
}
return values.includes(item.dataset[type]);
}
// If the item doesn't have a data attribute for this type, it doesn't match
return false;
});
});

// Update the display of list items based on the intersection results
listItems.forEach((item) => {
item.style.display = intersectionResults.includes(item) ? "block" : "none";
});
}

function getQueryParams(checkedFilters) {
// Group objects by type
const groupedData = checkedFilters.reduce((acc, obj) => {
if (!acc[obj.type]) {
acc[obj.type] = [];
}
acc[obj.type].push(obj.value);
return acc;
}, {});

// Construct query parameters
let queryParams = "";
Object.keys(groupedData).forEach((key) => {
const values = groupedData[key].join("%2B");
queryParams += `&${key}=${values}`;
});

// Append '?' to the beginning if there are query parameters
if (queryParams.length > 0) {
queryParams = "?" + queryParams.substring(1);
}
return queryParams;
}
93 changes: 93 additions & 0 deletions assets/js/tabs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const tabs = document.querySelectorAll('button[data-bs-toggle="tab"]');
const tabPanes = document.querySelectorAll('.tab-pane');

const activateTab = (tab) => {
tab.classList.add('active');
tab.setAttribute('aria-selected', 'true');
tab.setAttribute('tabindex', '0');

const tabId = tab.getAttribute('id');
tabPanes.forEach((tabPane) => {
if (tabPane.getAttribute('aria-labelledby') === tabId) {
tabPane.classList.add('active');
tabPane.classList.add('show');
}
});
};

const deactivateTab = (tab) => {
tab.classList.remove('active');
tab.setAttribute('aria-selected', 'false');
tab.setAttribute('tabindex', '-1');

const tabId = tab.getAttribute('id');
tabPanes.forEach((tabPane) => {
if (tabPane.getAttribute('aria-labelledby') === tabId) {
tabPane.classList.remove('active');
tabPane.classList.remove('show');
}
});
};

const activateFirstTab = () => {
const activeTabs = document.querySelectorAll('.nav-tabs');
activeTabs.forEach((activeTab) => {
const hasActiveTab = activeTab.querySelector(
"button[aria-selected='true']"
);
if (!hasActiveTab) {
const firstTab = activeTab.querySelector('button');
activateTab(firstTab);
}
});
};

const activeTab = localStorage.getItem('activeTab');
if (activeTab) {
tabs.forEach((tab) => {
if (tab.innerHTML === activeTab) {
activateTab(tab);
} else {
deactivateTab(tab);
}
});
activateFirstTab();
}

const reinitMermaidInTab = (tabPane) => {
const mermaidDiagrams = tabPane.querySelectorAll('.language-mermaid');
if (mermaidDiagrams.length === 0) return;

mermaidDiagrams.forEach((ele) => {
ele.innerHTML = ele.getAttribute('data-src') || '';
ele.removeAttribute('data-processed');
});

if (localStorage.getItem('theme') === 'dark') {
window.mermaid.init(window.darkModeConfig || {}, mermaidDiagrams);
} else {
window.mermaid.init(window.lightModeConfig || {}, mermaidDiagrams);
}
};

tabs.forEach((tab) => {
tab.addEventListener('click', () => {
const innerHTML = tab.innerHTML;
localStorage.setItem('activeTab', innerHTML);
tabs.forEach((tab) => {
if (tab.innerHTML === innerHTML) {
activateTab(tab);

const tabId = tab.getAttribute('id');
tabPanes.forEach((tabPane) => {
if (tabPane.getAttribute('aria-labelledby') === tabId) {
setTimeout(() => reinitMermaidInTab(tabPane), 10);
}
});
} else {
deactivateTab(tab);
}
});
activateFirstTab();
});
});
Loading