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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Import links and folders from a gethomepage or Dashy YAML config.

- The Docker Hub repository description is published from `README.md` on each
stable release.

Expand All @@ -17,6 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Confirming an import and naming a new folder use in-page dialogs instead of
the browser's own.

- The container prints six lines when it starts, instead of twenty-three.

- Nginx request errors now appear in `docker logs`, and Nginx no longer writes
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Then run `stackyard:local` the same way as above. For working on the code withou

Everything is configured in the admin UI (`/admin`), split into a few sections:

- **General**: title, description, and your server's **Host IP** (used to allow your own server in badge URLs and SSRF checks). Also **Monitoring** (logging level, Docker container health checks, and the socket URL used to reach Docker), optional **password protection**, and config **import / export**.
- **General**: title, description, and your server's **Host IP** (used to allow your own server in badge URLs and SSRF checks). Also **Monitoring** (logging level, Docker container health checks, and the socket URL used to reach Docker), optional **password protection**, and config **import / export**. Backup also imports a gethomepage or Dashy YAML config: links and folders are added to the dashboard, existing items are left alone, and anything that cannot be carried across is listed before you confirm.
- **Appearance**: wallpaper and the overall look.
- **Dashboard**: add and arrange your apps, folders, and widgets, and configure each one (including live activity badges).
- **About**: version and links.
Expand Down
18 changes: 18 additions & 0 deletions tsconfig.frontend.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@
"/js/html.js?v=*": [
"./ui/js/html.js"
],
"/js/import-foreign.js": [
"./ui/js/import-foreign.js"
],
"/js/import-foreign.js?v=*": [
"./ui/js/import-foreign.js"
],
"/js/i18n-markup.js": [
"./ui/js/i18n-markup.js"
],
Expand All @@ -160,6 +166,12 @@
"/js/link-url.js?v=*": [
"./ui/js/link-url.js"
],
"/js/modal.js": [
"./ui/js/modal.js"
],
"/js/modal.js?v=*": [
"./ui/js/modal.js"
],
"/js/mobile-metrics.js": [
"./ui/js/mobile-metrics.js"
],
Expand Down Expand Up @@ -190,6 +202,12 @@
"/js/utils.js?v=*": [
"./ui/js/utils.js"
],
"/js/yaml-lite.js": [
"./ui/js/yaml-lite.js"
],
"/js/yaml-lite.js?v=*": [
"./ui/js/yaml-lite.js"
],
"/js/widget-config-form.js": [
"./ui/js/widget-config-form.js"
],
Expand Down
6 changes: 4 additions & 2 deletions ui/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<meta name="theme-color" content="#0d1117">
<title>Stackyard Settings</title>
<link rel="stylesheet" href="/css/tokens.css?v=e24110a8">
<link rel="stylesheet" href="/css/admin.css?v=218a7495">
<link rel="stylesheet" href="/css/admin.css?v=5e0f6ef0">
<link rel="stylesheet" href="/css/widget-config-form.css?v=a9417164">
</head>
<body>
Expand Down Expand Up @@ -107,7 +107,9 @@
<div class="grp-hdr" data-i18n="general.backup">Backup</div>
<div class="grp">
<div class="row"><span class="rl" data-i18n="general.importExport">Import / Export</span><div class="row-btns"><label class="btn bg sm cursor-ptr"><span data-i18n="common.import">Import</span><input type="file" id="imp" accept=".json" class="file-hidden" aria-label="Import configuration JSON"></label><button class="btn bg sm" id="btn-exp" type="button" data-i18n="common.export">Export</button></div></div>
<div class="row"><span class="rl" data-i18n="general.importForeign">Import from another dashboard</span><div class="row-btns"><label class="btn bg sm cursor-ptr"><span data-i18n="common.chooseFiles">Choose files</span><input type="file" id="imp-foreign" accept=".yml,.yaml" multiple class="file-hidden" aria-label="Import Homepage or Dashy YAML config"></label></div></div>
</div>
<p class="grp-tip" data-i18n="general.importForeignTip">Reads gethomepage and Dashy YAML files. Links and folders are added to your dashboard; widgets from the other dashboard become plain links.</p>
<div class="signout-row"><button class="btn bd-btn d-none" id="sec-logout" type="button" data-i18n="general.signOut">Sign out</button></div>
</section>

Expand Down Expand Up @@ -283,6 +285,6 @@ <h1 class="sec-title" id="ev-title">General</h1>
</div></div>
</div>

<script type="module" src="/js/admin.js?v=5ea17d41"></script>
<script type="module" src="/js/admin.js?v=453c0383"></script>
</body>
</html>
22 changes: 15 additions & 7 deletions ui/css/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -341,12 +341,21 @@ html{background:var(--bg-color,var(--bg-outer))}
.sub{overflow:hidden;max-height:0;transition:max-height .25s ease;padding-inline-start:4px}
.sub.open{max-height:2000px}

/* ── Folder picker ── */
.fp-ov{position:fixed;inset:0;z-index:600;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;padding:24px}
.fp-box{background:var(--bg-outer);border:1px solid var(--bd);border-radius:var(--r);width:100%;max-width:380px;max-height:70vh;display:flex;flex-direction:column;box-shadow:0 16px 48px rgba(0,0,0,.6)}
.fp-hdr{padding:16px 20px;font-size:15px;font-weight:700;border-bottom:1px solid var(--bd-inner)}
.fp-list{overflow-y:auto;flex:1;padding:8px}
.fp-foot{padding:12px 16px;border-top:1px solid var(--bd-inner);display:flex;justify-content:flex-end}
/* ── Modal dialog (folder picker, import preview, prompts) ── */
.dlg-ov{position:fixed;inset:0;z-index:600;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;padding:24px}
.dlg-box{background:var(--bg-outer);border:1px solid var(--bd);border-radius:var(--r);width:100%;max-width:380px;max-height:70vh;display:flex;flex-direction:column;box-shadow:0 16px 48px rgba(0,0,0,.6)}
.dlg-ov.wide .dlg-box{max-width:560px;max-height:80vh}
.dlg-hdr{padding:16px 20px;font-size:15px;font-weight:700;border-bottom:1px solid var(--bd-inner)}
.dlg-body{overflow-y:auto;flex:1;padding:8px}
.dlg-foot{padding:12px 16px;border-top:1px solid var(--bd-inner);display:flex;justify-content:flex-end;gap:8px}
.dlg-field{display:flex;flex-direction:column;gap:6px;padding:12px}
.dlg-field span{font-size:13px;color:var(--dm)}
.dlg-lead{padding:8px 12px 4px;font-size:14px}
.dlg-sec{padding:12px 12px 4px;font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--dm)}
.dlg-ul{list-style:none;margin:0;padding:0 12px 8px;display:flex;flex-direction:column;gap:4px}
.dlg-li{display:flex;align-items:baseline;gap:8px;font-size:13px}
.dlg-li .dlg-why{color:var(--dm);font-size:12px}
.dlg-empty{padding:24px;text-align:center;color:var(--dm);font-size:13px}
.fp-row{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:var(--rs);background:none;border:none;color:var(--tx);font-size:14px;cursor:pointer;width:100%;text-align:start;font-family:inherit;transition:background var(--t)}
.fp-row:hover{background:rgba(255,255,255,.06)}
.fp-row.cur{background:rgba(2,122,255,.12)}
Expand All @@ -356,7 +365,6 @@ html{background:var(--bg-color,var(--bg-outer))}
.fp-ic img{width:18px;height:18px;object-fit:contain}
.fp-nm{flex:1}
.fp-chk{color:var(--gn);font-weight:700}
.fp-empty{padding:24px;text-align:center;color:var(--dm);font-size:13px}

/* ── Icon picker ── */
.ipw{position:relative}
Expand Down
52 changes: 47 additions & 5 deletions ui/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
"showPassword": "Passwort anzeigen",
"hidePassword": "Passwort verbergen",
"edit": "Bearbeiten",
"color": "Farbe"
"color": "Farbe",
"create": "Erstellen",
"chooseFiles": "Dateien wählen"
},
"general": {
"title": "Titel",
Expand All @@ -44,7 +46,9 @@
"passwordTip": "Leer lassen, um das bestehende Passwort beizubehalten. Mindestens 8 Zeichen. Für stärkeren Schutz solltest du das Dashboard hinter einem <strong>Authentication Proxy</strong> platzieren. Dieses integrierte Passwort ist nur eine leichte Absicherung, keine vollständige Authentifizierungsebene.",
"backup": "Sicherung",
"importExport": "Import / Export",
"signOut": "Abmelden"
"signOut": "Abmelden",
"importForeign": "Aus anderem Dashboard importieren",
"importForeignTip": "Liest YAML-Dateien von gethomepage und Dashy. Links und Ordner werden deinem Dashboard hinzugefügt; Widgets des anderen Dashboards werden zu einfachen Links."
},
"appearance": {
"appTitle": "App-Titel",
Expand Down Expand Up @@ -85,7 +89,12 @@
"noApps": "Keine Apps verfügbar",
"tip": "Tippen, um Apps in diesem Ordner hinzuzufügen oder zu entfernen.",
"namePh": "Mein Ordner",
"addAppToFolder": "App zu diesem Ordner hinzufügen"
"addAppToFolder": "App zu diesem Ordner hinzufügen",
"moveTo": "\"{name}\" in Ordner verschieben",
"addApp": "App zum Ordner hinzufügen",
"none": "Kein Ordner",
"allInFolder": "Alle Apps sind bereits in diesem Ordner.",
"createNew": "Neuen Ordner erstellen"
},
"app": {
"name": "Name",
Expand Down Expand Up @@ -159,7 +168,11 @@
"importNoChange": "Nichts zu importieren — die Datei stimmt mit deiner aktuellen Konfiguration überein.",
"pwWeak": "Passwort zu schwach: {label}",
"sessionsRevoked": "Alle Geräte wurden abgemeldet.",
"authNeedsPassword": "Legen Sie ein Passwort fest, bevor Sie die Authentifizierung aktivieren."
"authNeedsPassword": "Legen Sie ein Passwort fest, bevor Sie die Authentifizierung aktivieren.",
"importForeignDone": "Import abgeschlossen. Apps: {apps}. Ordner: {folders}.",
"importForeignNothing": "Aus diesen Dateien gibt es nichts zu importieren.",
"importYamlUnsupported": "{file}: {reason} (Zeile {line})",
"importUnknownFormat": "{file} ist keine gethomepage- oder Dashy-Konfiguration."
},
"confirm": {
"deleteFolder": "Ordner „{name}“ löschen? Enthaltene Apps werden nicht gelöscht.",
Expand All @@ -172,7 +185,8 @@
"noMatches": "Keine Treffer."
},
"import": {
"confirm": "{n} Elemente importieren?\n\n{added} hinzugefügt, {updated} aktualisiert, {deleted} gelöscht."
"confirm": "{n} Elemente importieren? {added} hinzugefügt, {updated} aktualisiert, {deleted} gelöscht.",
"confirmTitle": "Konfiguration importieren"
},
"status": {
"needsAttention": "Status: benötigt Aufmerksamkeit",
Expand Down Expand Up @@ -215,5 +229,33 @@
"refusedPlural": "{n} Widgets konnten nicht geladen werden und werden unten nicht aufgeführt.",
"name": "Name",
"type": "Widget-Typ"
},
"importForeign": {
"title": "Aus anderem Dashboard importieren",
"lead": "An deinem bestehenden Dashboard ändert sich nichts.",
"willAdd": "Hinzuzufügende Apps",
"willCreate": "Anzulegende Ordner",
"changed": "Mit Änderungen importiert",
"notImported": "Nicht importiert",
"appCount": "({n})",
"skipNoLabel": "kein Name",
"skipNoHref": "kein Link",
"skipUnsafeHref": "nicht unterstütztes Link-Schema",
"skipPlaceholderHref": "der Link braucht eine Variable, die nur das andere Dashboard kennt",
"noteIcon": "Symbol verworfen: {detail}",
"notePing": "ICMP-Ping verworfen: {detail}",
"noteRemoteContainer": "Container ignoriert, er läuft auf {detail}",
"noteFlattened": "verschachtelte Gruppe wurde ein eigener Ordner",
"noteSubItems": "Untereintrag in den Ordner verschoben",
"noteWidgetLink": "Widget als Link importiert",
"noteWidgetDropped": "{detail} Bereichs-Widgets verworfen",
"noteFields": "Felder verworfen: {detail}",
"notePages": "{detail} weitere Seiten nicht verfolgt",
"noteLocalUrl": "zweite lokale Adresse verworfen: {detail}",
"wholeFile": "diese Datei",
"untitledFolder": "Importiert",
"skipRelativeHref": "der Link hat keine Adresse, nur einen Pfad",
"skipUnreadable": "dieser Eintrag hat nicht die Form, die das Dateiformat erlaubt",
"monitors": "· fragt {url} ab"
}
}
52 changes: 47 additions & 5 deletions ui/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
"showPassword": "Show password",
"hidePassword": "Hide password",
"edit": "Edit",
"color": "Color"
"color": "Color",
"create": "Create",
"chooseFiles": "Choose files"
},
"general": {
"title": "Title",
Expand All @@ -44,7 +46,9 @@
"passwordTip": "Leave blank to keep existing password. Min 8 characters. For stronger protection, consider placing the dashboard behind a <strong>dedicated authentication proxy</strong>. This built-in password is a lightweight gate, not a full authentication layer.",
"backup": "Backup",
"importExport": "Import / Export",
"signOut": "Sign out"
"signOut": "Sign out",
"importForeign": "Import from another dashboard",
"importForeignTip": "Reads gethomepage and Dashy YAML files. Links and folders are added to your dashboard; widgets from the other dashboard become plain links."
},
"appearance": {
"appTitle": "App Title",
Expand Down Expand Up @@ -85,7 +89,12 @@
"noApps": "No apps available",
"tip": "Tap to add or remove apps from this folder.",
"namePh": "My Folder",
"addAppToFolder": "Add app to this folder"
"addAppToFolder": "Add app to this folder",
"moveTo": "Move \"{name}\" to folder",
"addApp": "Add app to folder",
"none": "No folder",
"allInFolder": "All apps are already in this folder.",
"createNew": "Create new folder"
},
"app": {
"name": "Name",
Expand Down Expand Up @@ -159,7 +168,11 @@
"importNoChange": "Nothing to import — the file matches your current config.",
"pwWeak": "Password too weak: {label}",
"sessionsRevoked": "All devices signed out.",
"authNeedsPassword": "Set a password before turning authentication on."
"authNeedsPassword": "Set a password before turning authentication on.",
"importForeignDone": "Import finished. Apps: {apps}. Folders: {folders}.",
"importForeignNothing": "Nothing to import from those files.",
"importYamlUnsupported": "{file}: {reason} (line {line})",
"importUnknownFormat": "{file} is not a gethomepage or Dashy config."
},
"confirm": {
"deleteFolder": "Delete folder \"{name}\"? Apps inside will not be deleted.",
Expand All @@ -172,7 +185,8 @@
"noMatches": "No matches."
},
"import": {
"confirm": "Import {n} items?\n\n{added} added, {updated} updated, {deleted} deleted."
"confirm": "Import {n} items? {added} added, {updated} updated, {deleted} deleted.",
"confirmTitle": "Import configuration"
},
"status": {
"needsAttention": "Status: needs attention",
Expand Down Expand Up @@ -215,5 +229,33 @@
"refusedPlural": "{n} widgets could not be loaded, so they are not listed below.",
"name": "Name",
"type": "Widget Type"
},
"importForeign": {
"title": "Import from another dashboard",
"lead": "Nothing already on your dashboard changes.",
"willAdd": "Apps to add",
"willCreate": "Folders to create",
"changed": "Imported with changes",
"notImported": "Not imported",
"appCount": "({n})",
"skipNoLabel": "no name",
"skipNoHref": "no link",
"skipUnsafeHref": "unsupported link scheme",
"skipPlaceholderHref": "the link needs a variable only the other dashboard can fill in",
"noteIcon": "icon dropped: {detail}",
"notePing": "ICMP ping dropped: {detail}",
"noteRemoteContainer": "container ignored, it runs on {detail}",
"noteFlattened": "nested group became its own folder",
"noteSubItems": "sub-item moved into the folder",
"noteWidgetLink": "widget imported as a link",
"noteWidgetDropped": "{detail} section widgets dropped",
"noteFields": "fields dropped: {detail}",
"notePages": "{detail} extra pages not followed",
"noteLocalUrl": "second local address dropped: {detail}",
"wholeFile": "this file",
"untitledFolder": "Imported",
"skipRelativeHref": "the link has no address, only a path",
"skipUnreadable": "this entry is not in a shape the file format allows",
"monitors": "· polls {url}"
}
}
Loading
Loading