Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
82 changes: 60 additions & 22 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ const SITE = 'https://stackyard.sandobserver.com';

export default defineConfig({
site: SITE,
redirects: {
'/docs/development': '/docs/contributing/',
'/docs/adding-services': '/docs/apps-and-folders/',
'/docs/advanced-configuration': '/docs/installation/docker/',
},
markdown: {
rehypePlugins: [
[rehypeExternalLinks, { target: '_blank', rel: ['noopener', 'noreferrer'] }],
Expand Down Expand Up @@ -72,23 +77,28 @@ export default defineConfig({
baseUrl: 'https://github.com/SandObserver/stackyard-docs/edit/main/',
},
sidebar: [
{ label: 'Introduction', link: '/docs/' },
{ label: 'Compare dashboards', link: '/docs/is-stackyard-for-you/' },
{
label: 'Installation',
label: 'Get started',
items: [
{ label: 'Docker', link: '/docs/installation/docker/' },
{ label: 'Unraid', link: '/docs/installation/unraid/' },
{ label: 'Introduction', link: '/docs/' },
{ label: 'Comparison', link: '/docs/is-stackyard-for-you/' },
{ label: 'Install with Docker', link: '/docs/installation/docker/' },
{ label: 'Install on Unraid', link: '/docs/installation/unraid/' },
{ label: 'Build from source', link: '/docs/installation/build-from-source/' },
{ label: 'First setup', link: '/docs/first-setup/' },
],
},
{
label: 'Build your dashboard',
items: [
{ label: 'Add apps and folders', link: '/docs/apps-and-folders/' },
{ label: 'Show status badges', link: '/docs/badges/' },
{ label: 'Wallpaper and themes', link: '/docs/customization/' },
],
},
{ label: 'First setup', link: '/docs/first-setup/' },
{ label: 'Settings reference', link: '/docs/settings-reference/' },
{ label: 'Advanced configuration', link: '/docs/advanced-configuration/' },
{ label: 'Adding services', link: '/docs/adding-services/' },
{ label: 'Badges', link: '/docs/badges/' },
{
label: 'Widgets',
collapsed: true,
items: [
{ label: 'Overview', link: '/docs/widgets/' },
{ label: 'Backup', link: '/docs/widgets/backup/' },
Expand All @@ -105,23 +115,51 @@ export default defineConfig({
{ label: 'Weather', link: '/docs/widgets/weather/' },
],
},
{ label: 'Customization', link: '/docs/customization/' },
{
label: 'Import and export',
label: 'Run Stackyard',
items: [
{ label: 'Backup and restore', link: '/docs/import-export/backup-and-restore/' },
{
label: 'Migrating from another dashboard',
link: '/docs/import-export/migrating/',
},
{ label: 'Back up and restore', link: '/docs/import-export/backup-and-restore/' },
{ label: 'Import', link: '/docs/import-export/migrating/' },
{ label: 'Use a reverse proxy', link: '/docs/reverse-proxy/' },
{ label: 'Security', link: '/docs/security/' },
],
},
{ label: 'Security', link: '/docs/security/' },
{ label: 'Accessibility', link: '/docs/accessibility/' },
{ label: 'Settings', link: '/docs/settings-reference/' },
{ label: 'Troubleshooting', link: '/docs/troubleshooting/' },
{ label: 'Support', link: '/docs/support/' },
{ label: 'Development', link: '/docs/development/' },
{ label: 'Changelog', link: '/docs/changelog/' },
{
label: 'Create a widget',
collapsed: true,
items: [
{ label: 'Build your first widget', link: '/docs/create-a-widget/' },
{ label: 'Manifest', link: '/docs/create-a-widget/manifest/' },
{ label: 'Data', link: '/docs/create-a-widget/data/' },
{ label: 'Widget page', link: '/docs/create-a-widget/widget-page/' },
{ label: 'Checklist', link: '/docs/create-a-widget/checklist/' },
],
},
{
label: 'Contributing',
collapsed: true,
items: [
{ label: 'How to contribute', link: '/docs/contributing/' },
{ label: 'Architecture', link: '/docs/contributing/architecture/' },
{ label: 'Design system', link: '/docs/contributing/design-system/' },
{ label: 'Translations', link: '/docs/contributing/translations/' },
{ label: 'API errors', link: '/docs/contributing/api-errors/' },
{ label: 'Releasing', link: '/docs/contributing/releasing/' },
],
},
{
label: 'About',
collapsed: true,
items: [
{ label: 'Accessibility', link: '/docs/accessibility/' },
{ label: 'Security policy', link: '/docs/security-policy/' },
{ label: 'Governance', link: '/docs/governance/' },
{ label: 'Support', link: '/docs/support/' },
{ label: 'Changelog', link: '/docs/changelog/' },
],
},
],
}),
mdx(),
Expand Down
6 changes: 6 additions & 0 deletions public/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/docs/development /docs/contributing/ 301
/docs/development/ /docs/contributing/ 301
/docs/adding-services /docs/apps-and-folders/ 301
/docs/adding-services/ /docs/apps-and-folders/ 301
/docs/advanced-configuration /docs/installation/docker/ 301
/docs/advanced-configuration/ /docs/installation/docker/ 301
1 change: 1 addition & 0 deletions public/api/widget-data/tpl-mywidget
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"items":[{"name":"First item"},{"name":"Second item"}],"total":12}
36 changes: 36 additions & 0 deletions public/img/stackyard-mark-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions public/widgets/mywidget/data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = async function (ctx) {
const { url, apiKey } = ctx.config;
if (!url) return { error: 'Not configured' };

const base = ctx.normalizeBase(url);
const r = await ctx.fetchJSON(`${base}/api/items`, {
headers: apiKey ? { 'X-Api-Key': apiKey } : {},
timeout: 8000,
});

return {
items: (r.data.items || []).slice(0, 10).map(i => ({ name: i.name })),
total: r.data.total ?? 0,
};
};
16 changes: 16 additions & 0 deletions public/widgets/mywidget/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* Optional. Only used when the dashboard runs with DEMO_MODE=true, where your
service is unreachable, so the widget is handed this body instead of running
data.js. Delete this file if you do not care how your widget looks there.

ctx is the same one data.js receives, plus ctx.demo. Using ctx.demo.wave for
anything that should look alive keeps your numbers drifting on the same clock
as every other widget's. */

module.exports = function (ctx) {
const { wave, round } = ctx.demo;
return {
items: [{ name: 'First item' }, { name: 'Second item' }],
total: Math.round(wave(600, 8, 20)),
ratio: round(wave(300, 0, 1), 2),
};
};
41 changes: 41 additions & 0 deletions public/widgets/mywidget/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="dark">
<meta name="robots" content="noindex">
<style>
*,*::before,*::after { margin:0; padding:0; box-sizing:border-box }
html,body { width:100%; height:100%; overflow:hidden; background:transparent;
font-family:-apple-system, BlinkMacSystemFont, system-ui, sans-serif; color:#e8e8ea }
#root { width:100%; height:100%; display:flex; flex-direction:column;
align-items:center; justify-content:center; gap:4px }
.n { font-size:34px; font-weight:600; letter-spacing:-0.5px }
.l { font-size:12px; color:#8e8e93 }
</style>
</head>
<body>
<div id="root"></div>
<script type="module">
import { poll } from '/js/widget-toolbox.js?v=1';

const root = document.getElementById('root');

poll({
render: data => {
root.replaceChildren();
const n = document.createElement('div');
n.className = 'n';
n.textContent = String(data.total);
const l = document.createElement('div');
l.className = 'l';
l.textContent = data.total === 1 ? 'item' : 'items';
root.append(n, l);
},
isEmpty: data => !data.items?.length,
emptyText: 'Nothing here',
interval: 30000,
});
</script>
</body>
</html>
11 changes: 11 additions & 0 deletions public/widgets/mywidget/widget.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "mywidget",
"label": "My Widget",
"sizes": ["small", "medium"],
"fields": [
{ "key": "url", "type": "text", "label": "Service URL",
"placeholder": "http://host:port" },
{ "key": "apiKey", "type": "secret", "label": "API key", "optional": true },
{ "key": "showTotal", "type": "toggle", "label": "Show total", "default": true }
]
}
10 changes: 6 additions & 4 deletions scripts/check-dist.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,15 @@ report('secrets', secretHits);
/* House style. */
report('style', files.filter((f) => f.startsWith('src') && read(f).includes('—')));

/* The development, changelog and accessibility pages are generated from the application
repository. When that checkout is absent they render a link instead, which
is a valid page and would otherwise pass every check above. */
/* The changelog, accessibility, security policy and governance pages are
generated from the application repository. When that checkout is absent they
render a link instead, which is a valid page and would otherwise pass every
check above. */
const generated = [
['dist/docs/development/index.html', 'CONTRIBUTING.md'],
['dist/docs/changelog/index.html', 'CHANGELOG.md'],
['dist/docs/accessibility/index.html', 'ACCESSIBILITY.md'],
['dist/docs/security-policy/index.html', 'SECURITY.md'],
['dist/docs/governance/index.html', 'GOVERNANCE.md'],
];
report(
'genpage',
Expand Down
3 changes: 2 additions & 1 deletion scripts/sync-widgets.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ const ROBOTS = '<meta name="robots" content="noindex">';
for (const [from, to] of [
[join(REPO, 'ui', 'widgets'), join(PUB, 'widgets')],
[join(REPO, 'ui', 'js'), join(PUB, 'js')],
[join(REPO, 'docs', 'widget-template'), join(PUB, 'widgets', 'mywidget')],
]) {
rmSync(to, { recursive: true, force: true });
cpSync(from, to, { recursive: true });
cpSync(from, to, { recursive: true, filter: (src) => !src.endsWith('README.md') });
}

function walk(dir, out = []) {
Expand Down
Loading