Skip to content

Commit 4a06eb4

Browse files
authored
Merge pull request #13 from eshtek/dev
Dev
2 parents 3cca410 + f371e1a commit 4a06eb4

9 files changed

Lines changed: 332 additions & 10 deletions

File tree

‎docs/.vitepress/sidebar.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,17 @@ const sidebar: DefaultTheme.Sidebar = {
7474
items: [
7575
{ text: 'Command Deck', link: '/release-notes/command-deck/',
7676
items: [
77+
// auto-generated-release-notes-start
78+
{ text: 'September 29, 2025', link: '/release-notes/command-deck/2025-09-29' },
79+
{ text: 'July 28, 2025', link: '/release-notes/command-deck/2025-07-28' },
80+
{ text: 'July 7, 2025', link: '/release-notes/command-deck/2025-07-07' },
81+
{ text: 'June 27, 2025', link: '/release-notes/command-deck/2025-06-27' },
7782
{ text: 'February 7, 2025', link: '/release-notes/command-deck/2025-02-07' },
7883
{ text: 'January 8, 2025', link: '/release-notes/command-deck/2025-01-08' },
7984
{ text: 'July 24, 2024', link: '/release-notes/command-deck/2024-07-24' },
8085
{ text: 'July 4, 2024', link: '/release-notes/command-deck/2024-07-04' },
8186
{ text: 'June 27, 2024', link: '/release-notes/command-deck/2024-06-27' },
87+
// auto-generated-release-notes-end
8288
]
8389
},
8490
]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Q2 Command Deck Update - June 27, 2025
2+
3+
## Key Features
4+
5+
### Manual Pool Configuration
6+
Customize your storage pools with granular control over drive participation. This even includes the ability to create 1-drive pools (without redundancy or expandability). This applies to both our setup wizard and the main storage page itself. Proper warnings are displayed based on the risk profile of the pool being configured. In addition, users that previously "disconnected" (this process is now referred to as "Unclaiming") their server from HexOS can easily reconnect through the setup wizard without being forced to recreate their storage pools from scratch (just click "skip" when on the storage configuration page of the wizard).
7+
8+
### New Activity Center
9+
The Health card has been replaced with Activity. Activity tracks everything you do in HexOS: our custom orchestration, server events, alerts, updates, and dismissals. Dismissed activities are reviewable from the activity archive.
10+
11+
### Update to TrueNAS SCALE 24.10.2.2
12+
Another benefit of the new activity center is the ability to update the OS running on your local server itself. You will find the ability to update the OS also in the new Activity Center on the Dashboard.
13+
14+
### User Settings & Preferences Improvements
15+
Light or dark theme, preferred language, hidden dashboard cards, these all now sync and persist across browser sessions. The dashboard is yours to shape and customize as you see fit.
16+
17+
### UI and Stability Improvements
18+
Improved card layout and polish across the UI, the password field now supports toggling visibility, username character limit raised to 30, and plenty of behind the scenes stability fixes.
19+
20+
### Customizable Location Preferences
21+
You can now set exactly your performance versus capacity pools, app locations and other storage preferences that allow our one click installs to work. Works across all curated and custom apps.
22+
23+
## How to Update
24+
25+
Updating the Command Deck itself is an action performed by the HexOS development team. Users are not required to perform any action to receive the update. It will appear automatically upon login after it goes live.
26+
27+
For users still running the 24.10.0.0 build of TrueNAS SCALE, you will see an "Update Available" notification under the Activity Center once you login. Once you engage the update, **do NOT power off your system**. Wait for the update to download and restart your server automatically. Your system will automatically reconnect to the Command Deck when complete. The time this process can take will vary based on your Internet connection.
28+
29+
## Known Issues
30+
31+
- There is a TrueNAS SCALE bug that affects HexOS users when switching your network configuration from DHCP to Static; manually setting DNS before making this change will prevent the issue; we have a planned hotfix for this as well.
32+
- Skipping storage pool creation in the wizard allows users to create pools within TrueNAS and they will show up in HexOS; **WARNING:** we have not tested all pool layouts (or sub-pools either), experiment with great caution.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Command Deck Update - July 7, 2025
2+
3+
We recently merged a few updates to our command deck to improve observability around request timing. This new instrumentation gives us better insight into performance and will help us identify and resolve issues more precisely.
4+
5+
No changes were made to the Command Deck UX in this release.
6+
7+
You might already notice things feel a bit snappier 😉 ... let us know.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Command Deck Update - July 28, 2025
2+
3+
Another Command Deck update went live today.
4+
5+
This release includes:
6+
7+
- Fixes to the networking layer for users switching between DHCP and static IP modes
8+
- Improved performance across the app based on the observability metrics added last release
9+
- A new queuing and caching layer to handle folder loading for users with a large number of folders, preventing API overload with TrueNAS
10+
- A fix for a bug that caused folders to appear missing when a private folder user had been deleted
11+
12+
Performance work is ongoing. Let us know if you spot anything.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Command Deck Update - September 29, 2025
2+
3+
New Apps, Curation Process and Folder updates
4+
5+
## Additional Apps
6+
We've just added Jellyfin, Home Assistant, and qBittorrent! And this is just the beginning, thanks to our new curation process, we expect many more apps to come online soon.
7+
8+
## Intro to App Curation
9+
We've added an interface to the HexOS frontend to make iterating on apps easier and to let the community contribute their own curations. By enabling Dev Mode in Preferences, you'll unlock the Custom Script option for app installs. From there, you can tweak existing install scripts or create new ones then share them with friends or submit them to the official HexOS curation templates.
10+
11+
Right now, the feature focuses on streamlining the app install process. Soon we plan to surface installer questions directly in the UI and automate post-install steps (such as in-app setup wizards). That work depends on the in-development Local UI project, we will share more news about this in the future.
12+
13+
We'd love your feedback as we refine this, let us know what's working well and where you're running into friction.
14+
15+
If you would like to contribute your curation to the official HexOS app curations you can follow this guide, or just share your install script in our [forums](https://hub.hexos.com/):
16+
1. Test your custom curation in HexOS and verify it works
17+
2. Click Here
18+
3. Click the green "Fork this repository"
19+
4. Paste the contents of your install script into the editor and press the green "Commit Changes"
20+
5. Click the green "Create pull request" on the following page
21+
6. In your PR description, include any callouts (unique mounts, GPU usage, special env, etc.)
22+
23+
## Folder Wizard Arrives
24+
We've extended our simple folder creation interface with a new wizard-driven process that unlocks specialized use cases like encrypted folders and Time Machine backups. With this foundation in place, we're now ready to move forward on bigger features including Snapshots, Replication, and Buddy Backup.
25+
26+
---
27+
28+
**NOTE:** This update was applied automatically. You may need to clear your cache. Help with clearing you cache is available [here](/troubleshooting/common-issues/ClearCache).

‎docs/release-notes/command-deck/index.md‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,22 @@ Command Deck updates are automatically deployed by the HexOS development team. Y
88

99
For users who are actively connected during an update, there may be a brief downtime of a minute or two as the new version is deployed.
1010

11+
<!-- auto-generated-year-sections-start -->
1112
## 2025 Releases
1213

13-
- [**February 7, 2025**](./2025-02-07) - Setup wizard improvements, data detection, UI enhancements
14-
- [**January 8, 2025**](./2025-01-08) - Manual WAN IP specification, UI improvements, bug fixes
14+
- [**September 29, 2025**](./2025-09-29) - Updates and improvements
15+
- [**July 28, 2025**](./2025-07-28) - Updates and improvements
16+
- [**July 7, 2025**](./2025-07-07) - Updates and improvements
17+
- [**June 27, 2025**](./2025-06-27) - Updates and improvements
18+
- [**February 7, 2025**](./2025-02-07) - Improved Setup Wizard Functionality, Existing Data Detection, Minor UI Improvements
19+
- [**January 8, 2025**](./2025-01-08) - Updates and improvements
1520

1621
## 2024 Releases
1722

18-
- [**July 24, 2024**](./2024-07-24) - Networking fixes, performance improvements, folder loading optimization
19-
- [**July 4, 2024**](./2024-07-04) - Performance monitoring improvements
20-
- [**June 27, 2024**](./2024-06-27) - Manual pool configuration, Activity Center, TrueNAS SCALE 24.10.2.2 update
21-
23+
- [**July 24, 2024**](./2024-07-24) - Networking Fixes, Performance Improvements, Folder Loading Optimization
24+
- [**July 4, 2024**](./2024-07-04) - Updates and improvements
25+
- [**June 27, 2024**](./2024-06-27) - Updates and improvements
26+
<!-- auto-generated-year-sections-end -->
2227

2328
## Need Help?
2429

‎package.json‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
"private": true,
55
"type": "module",
66
"scripts": {
7-
"gen:curated": "node scripts/generate-curated-index.mjs",
8-
"docs:dev": "npm run gen:curated && vitepress dev docs",
7+
"gen:curated": "node scripts/generateCuratedIndex.mjs",
8+
"gen:release-notes": "node scripts/generateReleaseNotesIndex.mjs",
9+
"gen:all": "npm run gen:curated && npm run gen:release-notes",
10+
"docs:dev": "npm run gen:all && vitepress dev docs",
911
"dev": "npm run docs:dev",
10-
"docs:dev:here": "DOCS_DIR=. node scripts/generate-curated-index.mjs && vitepress dev .",
11-
"docs:build": "npm run gen:curated && vitepress build docs",
12+
"docs:dev:here": "DOCS_DIR=. node scripts/generateCuratedIndex.mjs && node scripts/generateReleaseNotesIndex.mjs && vitepress dev .",
13+
"docs:build": "npm run gen:all && vitepress build docs",
1214
"docs:serve": "vitepress serve docs"
1315
},
1416
"devDependencies": {
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
#!/usr/bin/env node
2+
import fs from 'fs'
3+
import path from 'path'
4+
import { fileURLToPath } from 'url'
5+
6+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
7+
const DOCS_DIR = process.env.DOCS_DIR || 'docs'
8+
9+
const RELEASE_NOTES_DIR = path.resolve(__dirname, '..', DOCS_DIR, 'release-notes/command-deck')
10+
const INDEX_MD = path.resolve(RELEASE_NOTES_DIR, 'index.md')
11+
const SIDEBAR_TS = path.resolve(__dirname, '..', DOCS_DIR, '.vitepress/sidebar.ts')
12+
13+
// Sidebar automation markers
14+
const SIDEBAR_START = '// auto-generated-release-notes-start'
15+
const SIDEBAR_END = '// auto-generated-release-notes-end'
16+
17+
// Dynamic year section markers
18+
const YEAR_SECTIONS_START = '<!-- auto-generated-year-sections-start -->'
19+
const YEAR_SECTIONS_END = '<!-- auto-generated-year-sections-end -->'
20+
21+
function parseReleaseNote(filePath) {
22+
try {
23+
const content = fs.readFileSync(filePath, 'utf8')
24+
const lines = content.split('\n')
25+
26+
// Get title from first heading
27+
const titleLine = lines.find(line => line.startsWith('# '))
28+
const title = titleLine ? titleLine.replace('# ', '') : 'Unknown'
29+
30+
// Extract date from filename (YYYY-MM-DD.md)
31+
const filename = path.basename(filePath, '.md')
32+
const dateMatch = filename.match(/(\d{4})-(\d{2})-(\d{2})/)
33+
34+
if (!dateMatch) return null
35+
36+
const [, year, month, day] = dateMatch
37+
const date = new Date(year, month - 1, day)
38+
const formattedDate = date.toLocaleDateString('en-US', {
39+
year: 'numeric',
40+
month: 'long',
41+
day: 'numeric'
42+
})
43+
44+
// Generate description from content (look for key features or main points)
45+
let description = ''
46+
let inKeyFeatures = false
47+
let features = []
48+
49+
for (const line of lines) {
50+
if (line.startsWith('## Key Features') || line.startsWith('## Additional Apps')) {
51+
inKeyFeatures = true
52+
continue
53+
}
54+
if (line.startsWith('##') && inKeyFeatures) {
55+
break
56+
}
57+
if (inKeyFeatures && line.startsWith('- ') || line.startsWith('### ')) {
58+
const feature = line.replace(/^- \*\*|\*\*|^### /g, '').split(' - ')[0].split(':')[0]
59+
if (feature && !features.includes(feature)) {
60+
features.push(feature)
61+
}
62+
}
63+
}
64+
65+
description = features.length > 0 ? features.slice(0, 3).join(', ') : 'Updates and improvements'
66+
67+
return {
68+
filename,
69+
title,
70+
formattedDate,
71+
description,
72+
year: parseInt(year),
73+
sortDate: date
74+
}
75+
} catch (error) {
76+
console.warn(`Could not parse ${filePath}:`, error.message)
77+
return null
78+
}
79+
}
80+
81+
function buildReleaseList(releases) {
82+
if (!releases.length) return '_No releases found._'
83+
84+
// Sort by date descending (newest first)
85+
releases.sort((a, b) => b.sortDate - a.sortDate)
86+
87+
return releases.map(release =>
88+
`- [**${release.formattedDate}**](./${release.filename}) - ${release.description}`
89+
).join('\n')
90+
}
91+
92+
function ensureIndexPage() {
93+
if (!fs.existsSync(INDEX_MD)) {
94+
fs.mkdirSync(path.dirname(INDEX_MD), { recursive: true })
95+
const boilerplate = `# Command Deck Release Notes
96+
97+
Stay up to date with HexOS Command Deck updates, new features, and improvements.
98+
99+
## How Updates Work
100+
101+
Command Deck updates are automatically deployed by the HexOS development team. You don't need to take any action to receive these updates - they will appear automatically when you next visit the Command Deck.
102+
103+
For users who are actively connected during an update, there may be a brief downtime of a minute or two as the new version is deployed.
104+
105+
${YEAR_SECTIONS_START}
106+
${YEAR_SECTIONS_END}
107+
108+
## Need Help?
109+
110+
If you encounter any issues after an update:
111+
- Try [clearing your browser cache](/troubleshooting/common-issues/ClearCache) first
112+
- Check the [troubleshooting guide](/troubleshooting/) for common solutions
113+
- Visit the [HexOS Community](https://hub.hexos.com/) for additional support
114+
`
115+
fs.writeFileSync(INDEX_MD, boilerplate)
116+
}
117+
}
118+
119+
function updateIndexPage() {
120+
ensureIndexPage()
121+
let md = fs.readFileSync(INDEX_MD, 'utf8')
122+
123+
// Ensure year sections markers exist
124+
if (!md.includes(YEAR_SECTIONS_START)) {
125+
// If no markers, add them after "How Updates Work" section
126+
const insertPoint = md.indexOf('## Need Help?')
127+
if (insertPoint !== -1) {
128+
const before = md.slice(0, insertPoint)
129+
const after = md.slice(insertPoint)
130+
md = `${before}${YEAR_SECTIONS_START}\n${YEAR_SECTIONS_END}\n\n${after}`
131+
}
132+
}
133+
134+
// Get all release note files
135+
const files = fs.readdirSync(RELEASE_NOTES_DIR)
136+
.filter(f => f.endsWith('.md') && f !== 'index.md' && f.match(/^\d{4}-\d{2}-\d{2}\.md$/))
137+
138+
const releases = files
139+
.map(f => parseReleaseNote(path.join(RELEASE_NOTES_DIR, f)))
140+
.filter(r => r !== null)
141+
142+
// Group by year and sort years descending (newest first)
143+
const releasesByYear = releases.reduce((acc, release) => {
144+
if (!acc[release.year]) acc[release.year] = []
145+
acc[release.year].push(release)
146+
return acc
147+
}, {})
148+
149+
const years = Object.keys(releasesByYear).map(Number).sort((a, b) => b - a)
150+
151+
// Generate year sections
152+
const yearSections = years.map(year => {
153+
const yearReleases = releasesByYear[year]
154+
const content = buildReleaseList(yearReleases)
155+
return `## ${year} Releases\n\n${content}`
156+
}).join('\n\n')
157+
158+
// Update the year sections
159+
if (md.includes(YEAR_SECTIONS_START) && md.includes(YEAR_SECTIONS_END)) {
160+
const before = md.slice(0, md.indexOf(YEAR_SECTIONS_START) + YEAR_SECTIONS_START.length)
161+
const after = md.slice(md.indexOf(YEAR_SECTIONS_END))
162+
md = `${before}\n${yearSections}\n${after}`
163+
164+
fs.writeFileSync(INDEX_MD, md)
165+
console.log('Wrote', INDEX_MD)
166+
167+
const yearCounts = years.map(year => `${releasesByYear[year].length} releases for ${year}`).join(', ')
168+
console.log(`Found ${yearCounts}`)
169+
} else {
170+
console.warn('Could not find year section markers in index.md')
171+
}
172+
}
173+
174+
function generateSidebarItems(releases) {
175+
if (!releases.length) return ''
176+
177+
// Sort by date descending (newest first)
178+
releases.sort((a, b) => b.sortDate - a.sortDate)
179+
180+
return releases.map(release =>
181+
` { text: '${release.formattedDate}', link: '/release-notes/command-deck/${release.filename}' },`
182+
).join('\n')
183+
}
184+
185+
function updateSidebar() {
186+
if (!fs.existsSync(SIDEBAR_TS)) {
187+
console.warn('Sidebar file not found, skipping sidebar update')
188+
return
189+
}
190+
191+
let sidebarContent = fs.readFileSync(SIDEBAR_TS, 'utf8')
192+
193+
// Get all release note files
194+
const files = fs.readdirSync(RELEASE_NOTES_DIR)
195+
.filter(f => f.endsWith('.md') && f !== 'index.md' && f.match(/^\d{4}-\d{2}-\d{2}\.md$/))
196+
197+
const releases = files
198+
.map(f => parseReleaseNote(path.join(RELEASE_NOTES_DIR, f)))
199+
.filter(r => r !== null)
200+
201+
const sidebarItems = generateSidebarItems(releases)
202+
203+
// Add markers if they don't exist
204+
if (!sidebarContent.includes(SIDEBAR_START)) {
205+
// Find the Command Deck items section and add markers
206+
const commandDeckMatch = sidebarContent.match(/({\s*text:\s*'Command Deck',\s*link:\s*'\/release-notes\/command-deck\/',\s*items:\s*\[)([\s\S]*?)(\]\s*},)/m)
207+
208+
if (commandDeckMatch) {
209+
const before = commandDeckMatch[1]
210+
const after = commandDeckMatch[3]
211+
const replacement = `${before}\n ${SIDEBAR_START}\n ${SIDEBAR_END}\n ${after}`
212+
sidebarContent = sidebarContent.replace(commandDeckMatch[0], replacement)
213+
}
214+
}
215+
216+
// Update the sidebar items between markers
217+
if (sidebarContent.includes(SIDEBAR_START) && sidebarContent.includes(SIDEBAR_END)) {
218+
const before = sidebarContent.slice(0, sidebarContent.indexOf(SIDEBAR_START) + SIDEBAR_START.length)
219+
const after = sidebarContent.slice(sidebarContent.indexOf(SIDEBAR_END))
220+
sidebarContent = `${before}\n${sidebarItems}\n ${after}`
221+
222+
fs.writeFileSync(SIDEBAR_TS, sidebarContent)
223+
console.log('Updated sidebar.ts with', releases.length, 'release notes')
224+
} else {
225+
console.warn('Could not find sidebar markers, manual update required')
226+
}
227+
}
228+
229+
updateIndexPage()
230+
updateSidebar()

0 commit comments

Comments
 (0)