Easy Setup wizard previously bundled AI model selection + the new
ingest-policy radio into Step 3 alongside Wikipedia/ZIM tiers and curated
content. Three problems with that:
1. Predicate divergence: "is AI selected?" was answered three different
ways across Step 3 radio, Step 4 review card, and handleFinish
persistence. Surfaced in @jakeaturner's review of PR #900. The three
predicates disagree in real cases (e.g. Ollama already installed but
user didn't re-select any models -- handleFinish writes the ingest KV
while the review hides the AI summary).
2. Step 3 was overloaded -- ZIM tiers + curated content + AI models +
ingest policy in one screen.
3. No way to opt out of seeing the AI policy radio when AI isn't part of
the user's setup.
This restructure makes step 4 a dedicated, conditional AI step:
Step 1 (Apps) -- unchanged (services + remote Ollama toggle/URL)
Step 2 (Maps) -- unchanged
Step 3 (Content) -- Wikipedia + curated tiers only
Step 4 (AI) -- NEW, conditional: model picker (or remote notice)
+ auto-index policy radio. Skipped entirely when
AI isn't in the setup.
Step 5 (Review) -- summary, reads back step 4's output via the same
canonical predicate
Decisions per issue #905 discussion:
- Canonical predicate `isAiInSetup` as a useMemo. Single source consumed
by step indicator, nav skip logic, review summary, and handleFinish.
Both prior divergence cases collapse.
- Step indicator renders dynamically: 4 dots when AI is off (positional
display numbers 1..4), 5 dots when AI is on. WizardStep semantic values
(1=Apps, 2=Maps, 3=Content, 4=AI, 5=Review) stay stable so nav handlers
don't have to translate; the dot's `displayNumber` is decoupled from
its `step` so users see sequential 1..N with no gap.
- handleNext / handleBack are symmetric: 3 -> 5 forward, 5 -> 3 back,
when !isAiInSetup. Same predicate gate.
- Toggling AI capability off in Step 1 after AI step selections were
made fires a confirm dialog ("Turning off AI will discard your AI
model picks, indexing policy, and remote Ollama configuration") and
clears selectedAiModels / ingestPolicy / remoteOllamaEnabled on
confirm. Silent clear when nothing was set.
- Remote Ollama toggle stays in Step 1 alongside the capability card.
Don't fragment "am I using remote AI?" across two steps.
The bundled review summary (renderStep5, was renderStep4) now uses
`isAiInSetup` for the auto-index card visibility instead of the
divergent `(selectedAiModels.length > 0 || remoteOllamaEnabled)`.
Inertia tsconfig clean for this file (the only outstanding errors are
the 3 KnowledgeBaseModal ones from issue tracked in PR #907 and the
~64 pre-existing errors elsewhere).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an inline auto-index policy choice inside the Easy Setup wizard's
existing AI section (Step 3 'Content', alongside AI model selection).
The selection is persisted to KVStore['rag.defaultIngestPolicy'] on
wizard submit — same key #894's KB modal toggle reads/writes — so a
user who completes the wizard never sees the first-chat JIT prompt
(#899); their decision is already recorded.
Default is 'Always' so new users who keep the default get the 'just
works' experience: content downloaded by the wizard becomes searchable
as soon as it finishes embedding, without a follow-up step. Users who
prefer the explicit-opt-in flow can flip to 'Manual' before submitting.
Skipped when the user doesn't select the AI capability — the KV stays
null and the JIT prompt handles the decision later if/when they enable
AI from settings.
UI placement
- Step 3 'Content': new section below AI Models grid (only when AI is
selected), two-button radio matching #894's KB-modal toggle pattern
for visual consistency
- Step 4 'Review': new 'Auto-index Setting' card summarizing the choice
in plain English ('New content will be indexed automatically' vs
'New content will wait for you to opt in') so the user knows what
they're agreeing to before clicking Complete Setup
handleFinish
- New api.updateSetting('rag.defaultIngestPolicy', ingestPolicy) call
runs first, before service installs/downloads, so any content that
finishes embedding during this same wizard run sees the right policy
- Wrapped in its own try/catch so a transient KV write failure doesn't
abort the rest of the wizard
Stacks on feat/kb-policy-toggle (#894) — uses the policy KV mechanism
that PR introduces. Auto-rebases to rc when #894 merges.
Pairs with #899 (JIT prompt): wizard users decide here; non-wizard
users decide at first chat. Together they cover every entry path
to v1.32.0 without double-prompting.
The disk-collector could produce an empty fsSize array when
/host/proc/1/mounts is unreadable, causing the admin UI to fall back
to systeminformation's fsSize which includes tmpfs mounts. This led to
the storage display showing ~1.5 GB (tmpfs /run) instead of the actual
storage capacity.
Two changes:
- disk-collector: fall back to df on /storage when host mount table
yields no real filesystems, since /storage is always bind-mounted
from the host and reflects the actual backing device.
- easy-setup UI: when falling back to systeminformation fsSize, filter
for /dev/ block devices and prefer the largest one instead of blindly
taking the first entry.
Fixes#373
Add a warm charcoal dark mode ("Night Ops") using CSS variable swapping
under [data-theme="dark"]. All 23 desert palette variables are overridden
with dark-mode counterparts, and ~313 generic Tailwind classes (bg-white,
text-gray-*, border-gray-*) are replaced with semantic tokens.
Infrastructure:
- CSS variable overrides in app.css for both themes
- ThemeProvider + useTheme hook (localStorage + KV store sync)
- ThemeToggle component (moon/sun icons, "Night Ops"/"Day Ops" labels)
- FOUC prevention script in inertia_layout.edge
- Toggle placed in StyledSidebar and Footer for access on every page
Color replacements across 50 files:
- bg-white → bg-surface-primary
- bg-gray-50/100 → bg-surface-secondary
- text-gray-900/800 → text-text-primary
- text-gray-600/500 → text-text-secondary/text-text-muted
- border-gray-200/300 → border-border-subtle/border-border-default
- text-desert-white → text-white (fixes invisible text on colored bg)
- Button hover/active states use dedicated btn-green-hover/active vars
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WikiHow ZIM files were deprecated by Kiwix after WikiHow requested
removal to protect their content from LLM training harvesting.
Replace with "DIY repair guides and how-to content" which accurately
reflects the iFixit, Stack Exchange, and other how-to content
available in NOMAD's curated collections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removes the InstalledTier model and instead checks presence of files on-the-fly. Avoid broken state by handling on the server-side vs. marking as installed by client-side API call
Adds a useEffect that smoothly scrolls the window to the top whenever
the wizard step changes. This ensures users always see the beginning
of each step content rather than remaining scrolled down from the
previous step.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a standalone Wikipedia selection section that appears prominently in both
the Easy Setup Wizard and Content Explorer. Features include:
- Six Wikipedia package options ranging from Quick Reference (313MB) to Complete
Wikipedia with Full Media (99.6GB)
- Card-based radio selection UI with clear size indicators
- Smart replacement: downloads new package before deleting old one
- Status tracking: shows Installed, Selected, or Downloading badges
- "No Wikipedia" option for users who want to skip or remove Wikipedia
Technical changes:
- New wikipedia_selections database table and model
- New /api/zim/wikipedia and /api/zim/wikipedia/select endpoints
- WikipediaSelector component with consistent styling
- Integration with existing download queue system
- Callback updates status to 'installed' on successful download
- Wikipedia removed from tiered category system to avoid duplication
UI improvements:
- Added section dividers and icons (AI Models, Wikipedia, Additional Content)
- Consistent spacing between major sections in Easy Setup Wizard
- Content Explorer gets matching Wikipedia section with submit button
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
System Benchmark is a built-in feature that doesn't require installation,
so it shouldn't appear in the Easy Setup Wizard where users select things
to install. Users can access the benchmark through Settings > Benchmark.
- Removed benchmark entry from ADDITIONAL_TOOLS array
- Removed unused isBuiltInCapability helper and related dead code
- Simplified renderCapabilityCard by removing built-in specific styling
- Removed unused IconArrowRight import
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add installed_tiers table to persist user's tier selection per category
- Change tier selection behavior: clicking a tier now highlights it locally,
user must click "Submit" to confirm (previously clicked = immediate download)
- Remove "Recommended" badge and asterisk (*) from tier displays
- Highlight installed tier instead of recommended tier in CategoryCard
- Add "Click to choose" hint when no tier is installed
- Save installed tier when downloading from Content Explorer or Easy Setup
- Pass installed tier to modal as default selection
Database:
- New migration: create installed_tiers table (category_slug unique, tier_slug)
- New model: InstalledTier
Backend:
- ZimService.listCuratedCategories() now includes installedTierSlug
- New ZimService.saveInstalledTier() method
- New POST /api/zim/save-installed-tier endpoint
Frontend:
- TierSelectionModal: local selection state, "Close" → "Submit" button
- CategoryCard: highlight based on installedTierSlug, add "Click to choose"
- Content Explorer: save tier after download, refresh categories
- Easy Setup: save tiers on wizard completion
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The storage projection bar was blindly using disk[0], which on systems
with multiple drives (like the Minisforum AI X1 Pro) could be an empty
or uninitialized drive (e.g., sda showing N/A / N/A).
Now the disk selection:
1. Filters out disks with totalSize === 0 (invalid/empty drives)
2. Prefers disk containing root (/) or /storage mount point
3. Falls back to largest valid disk if no root mount found
This fixes the NaN% and 0 Bytes display on multi-drive systems.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive benchmarking capability to measure server performance:
Backend:
- BenchmarkService with CPU, memory, disk, and AI benchmarks using sysbench
- Database migrations for benchmark_results and benchmark_settings tables
- REST API endpoints for running benchmarks and retrieving results
- CLI commands: benchmark:run, benchmark:results, benchmark:submit
- BullMQ job for async benchmark execution with SSE progress updates
- Synchronous mode option (?sync=true) for simpler local dev setup
Frontend:
- Benchmark settings page with circular gauges for scores
- NOMAD Score display with weighted composite calculation
- System Performance section (CPU, Memory, Disk Read/Write)
- AI Performance section (tokens/sec, time to first token)
- Hardware Information display
- Expandable Benchmark Details section
- Progress simulation during sync benchmark execution
Easy Setup Integration:
- Added System Benchmark to Additional Tools section
- Built-in capability pattern for non-Docker features
- Click-to-navigate behavior for built-in tools
Fixes:
- Docker log multiplexing issue (Tty: true) for proper output parsing
- Consolidated disk benchmarks into single container execution
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace technical app names with user-friendly capability categories:
- "Information Library" (Kiwix) - offline Wikipedia, medical refs, etc.
- "Education Platform" (Kolibri) - Khan Academy, K-12 content
- "AI Assistant" (Open WebUI + Ollama) - local AI chat
- Add bullet point feature lists for each core capability
- Move secondary apps (Notes, Data Tools) to collapsible "Additional Tools"
- Show already-installed capabilities with "Installed" badge and disabled state
- Update terminology: "capabilities" instead of "apps", "content packs" instead of "ZIM collections"
- Update Review step to show capability names with technical names in parentheses
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jake Turner <52841588+jakeaturner@users.noreply.github.com>
Adds a dynamic storage projection bar that shows users how their
selections will impact disk space:
- Displays current disk usage and projected usage after installation
- Updates in real-time as users select maps, ZIM collections, and tiers
- Color-coded warnings (green→tan→orange→red) based on projected usage
- Shows "exceeds available space" warning if selections exceed capacity
- Works on both Linux (disk array) and Windows (fsSize array)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
jsDelivr CDN was aggressively caching old data during development.
Raw GitHub URLs provide more immediate updates when pushing changes
to the feature branch.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
jsDelivr aggressively caches branch references. Adding version
parameter ensures fresh data is fetched when categories are updated.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
GitHub raw URLs don't allow cross-origin requests from localhost.
Using jsDelivr CDN which serves GitHub content with proper CORS headers.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add kiwix-categories.json with Medicine category and 3 tiers
- Create CategoryCard component for displaying category cards
- Create TierSelectionModal for tier selection UI
- Integrate categories into Easy Setup wizard (Step 3)
- Add TypeScript types for categories and tiers
- Fallback to legacy flat collections if categories unavailable
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>