mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-04-05 16:26:15 +02:00
Merge branch 'master' into feature/easy-setup-wizard-ux
This commit is contained in:
commit
e0dcd129e6
195
admin/docs/faq.md
Normal file
195
admin/docs/faq.md
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
# Frequently Asked Questions
|
||||
|
||||
## General Questions
|
||||
|
||||
### What is N.O.M.A.D.?
|
||||
N.O.M.A.D. (Node for Offline Media, Archives, and Data) is a personal server that gives you access to knowledge, education, and AI assistance without requiring an internet connection. It runs on your own hardware, keeping your data private and accessible anytime.
|
||||
|
||||
### Do I need internet to use N.O.M.A.D.?
|
||||
No — that's the whole point. Once your content is downloaded, everything works offline. You only need internet to:
|
||||
- Download new content
|
||||
- Update the software
|
||||
- Sync the latest versions of Wikipedia, maps, etc.
|
||||
|
||||
### What hardware do I need?
|
||||
N.O.M.A.D. is designed for capable hardware, especially if you want to use the AI features. Recommended:
|
||||
- Modern multi-core CPU
|
||||
- 16GB+ RAM (32GB+ for best AI performance)
|
||||
- SSD storage (size depends on content — 500GB minimum, 2TB+ recommended)
|
||||
- GPU recommended for faster AI responses
|
||||
|
||||
### How much storage do I need?
|
||||
It depends on what you download:
|
||||
- Full Wikipedia: ~95GB
|
||||
- Khan Academy courses: ~50GB
|
||||
- Medical references: ~500MB
|
||||
- US state maps: ~2-3GB each
|
||||
- AI models: 10-40GB depending on model
|
||||
|
||||
Start with essentials and add more as needed.
|
||||
|
||||
---
|
||||
|
||||
## Content Questions
|
||||
|
||||
### How do I add more Wikipedia content?
|
||||
1. Go to **Settings** (hamburger menu → Settings)
|
||||
2. Click **ZIM Manager**
|
||||
3. Browse available content
|
||||
4. Click Download on items you want
|
||||
|
||||
### How do I add more educational courses?
|
||||
1. Open **Kolibri**
|
||||
2. Sign in as an admin
|
||||
3. Go to **Device → Channels**
|
||||
4. Browse and import available channels
|
||||
|
||||
### How current is the content?
|
||||
Content is as current as when it was last downloaded. Wikipedia snapshots are typically updated monthly. Check the file names or descriptions for dates.
|
||||
|
||||
### Can I add my own files?
|
||||
Currently, N.O.M.A.D. uses standard content formats (ZIM files for Kiwix, Kolibri channels for education). Custom content support may be added in future versions.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### A feature isn't loading or shows a blank page
|
||||
|
||||
**Try these steps:**
|
||||
1. Wait 30 seconds — some features take time to start
|
||||
2. Refresh the page (Ctrl+R or Cmd+R)
|
||||
3. Go back to the Command Center and try again
|
||||
4. Check Settings → System to see if the service is running
|
||||
5. Try restarting the service (Stop, then Start in Apps manager)
|
||||
|
||||
### Maps show a gray/blank area
|
||||
|
||||
The Maps feature requires downloaded map data. If you see a blank area:
|
||||
1. Go to **Settings → Maps Manager**
|
||||
2. Download map regions for your area
|
||||
3. Wait for downloads to complete
|
||||
4. Return to Maps and refresh
|
||||
|
||||
### AI responses are slow
|
||||
|
||||
Local AI requires significant computing power. To improve speed:
|
||||
- Close other applications on the server
|
||||
- Ensure adequate cooling (overheating causes throttling)
|
||||
- Consider using a smaller/faster AI model if available
|
||||
- Add a GPU if your hardware supports it
|
||||
|
||||
### "Service unavailable" or connection errors
|
||||
|
||||
The service might still be starting up. Wait 1-2 minutes and try again.
|
||||
|
||||
If the problem persists:
|
||||
1. Go to **Settings → Apps**
|
||||
2. Find the problematic service
|
||||
3. Click **Restart**
|
||||
4. Wait 30 seconds, then try again
|
||||
|
||||
### Downloads are stuck or failing
|
||||
|
||||
1. Check your internet connection
|
||||
2. Go to **Settings** and check available storage
|
||||
3. If storage is full, delete unused content
|
||||
4. Cancel the stuck download and try again
|
||||
|
||||
### The server won't start
|
||||
|
||||
If you can't access the Command Center at all:
|
||||
1. Verify the server hardware is powered on
|
||||
2. Check network connectivity
|
||||
3. Try accessing directly via the server's IP address
|
||||
4. Check server logs if you have console access
|
||||
|
||||
### I forgot my Kolibri password
|
||||
|
||||
Kolibri passwords are managed separately:
|
||||
1. If you're an admin, you can reset user passwords in Kolibri's user management
|
||||
2. If you forgot the admin password, you may need to reset it via command line (contact your administrator)
|
||||
|
||||
---
|
||||
|
||||
## Updates and Maintenance
|
||||
|
||||
### How do I update N.O.M.A.D.?
|
||||
1. Go to **Settings → Check for Updates**
|
||||
2. If an update is available, click to install
|
||||
3. The system will download updates and restart automatically
|
||||
4. This typically takes 2-5 minutes
|
||||
|
||||
### Should I update regularly?
|
||||
Yes, while you have internet access. Updates include:
|
||||
- Bug fixes
|
||||
- New features
|
||||
- Security improvements
|
||||
- Performance enhancements
|
||||
|
||||
### How do I update content (Wikipedia, etc.)?
|
||||
Content updates are separate from software updates:
|
||||
1. Go to **Settings → ZIM Manager**
|
||||
2. Check for newer versions of your installed content
|
||||
3. Download updated versions as needed
|
||||
|
||||
Tip: New Wikipedia snapshots are released approximately monthly.
|
||||
|
||||
### What happens if an update fails?
|
||||
The system is designed to recover gracefully. If an update fails:
|
||||
1. The previous version should continue working
|
||||
2. Try the update again later
|
||||
3. Check Settings → System for error messages
|
||||
|
||||
### Command-Line Maintenance
|
||||
|
||||
For advanced troubleshooting or when you can't access the web interface, N.O.M.A.D. includes helper scripts in `/opt/project-nomad`:
|
||||
|
||||
**Start all services:**
|
||||
```bash
|
||||
sudo bash /opt/project-nomad/start_nomad.sh
|
||||
```
|
||||
|
||||
**Stop all services:**
|
||||
```bash
|
||||
sudo bash /opt/project-nomad/stop_nomad.sh
|
||||
```
|
||||
|
||||
**Update Command Center:**
|
||||
```bash
|
||||
sudo bash /opt/project-nomad/update_nomad.sh
|
||||
```
|
||||
*Note: This updates the Command Center only, not individual apps. Update apps through the web interface.*
|
||||
|
||||
**Uninstall N.O.M.A.D.:**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/master/install/uninstall_nomad.sh -o uninstall_nomad.sh
|
||||
sudo bash uninstall_nomad.sh
|
||||
```
|
||||
*Warning: This cannot be undone. All data will be deleted.*
|
||||
|
||||
---
|
||||
|
||||
## Privacy and Security
|
||||
|
||||
### Is my data private?
|
||||
Yes. N.O.M.A.D. runs entirely on your hardware. Your searches, AI conversations, and usage data never leave your server.
|
||||
|
||||
### Can others access my server?
|
||||
By default, N.O.M.A.D. is accessible on your local network. Anyone on the same network can access it. For public networks, consider additional security measures.
|
||||
|
||||
### Does the AI send data anywhere?
|
||||
No. The AI runs completely locally. Your conversations are not sent to any external service.
|
||||
|
||||
---
|
||||
|
||||
## Getting More Help
|
||||
|
||||
### The AI can help
|
||||
Try asking Open WebUI for help. The local AI can answer questions about many topics, including technical troubleshooting.
|
||||
|
||||
### Check the documentation
|
||||
You're in the docs now. Use the menu to find specific topics.
|
||||
|
||||
### Release Notes
|
||||
See what's changed in each version: **[Release Notes](/docs/release-notes)**
|
||||
238
admin/docs/getting-started.md
Normal file
238
admin/docs/getting-started.md
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
# Getting Started with N.O.M.A.D.
|
||||
|
||||
This guide will help you install and set up your N.O.M.A.D. server.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### System Requirements
|
||||
|
||||
N.O.M.A.D. runs on any **Debian-based Linux** system (Ubuntu recommended). The installation is terminal-based, and everything is accessed through a web browser — no desktop environment needed.
|
||||
|
||||
**Minimum Specs** (Command Center only):
|
||||
- 2 GHz dual-core processor
|
||||
- 4 GB RAM
|
||||
- 5 GB free storage
|
||||
- Internet connection (for initial install)
|
||||
|
||||
**Recommended Specs** (with AI features):
|
||||
- AMD Ryzen 7 / Intel Core i7 or better
|
||||
- 32 GB RAM
|
||||
- NVIDIA RTX 3060 or better (more VRAM = larger AI models)
|
||||
- 250 GB+ free storage (SSD preferred)
|
||||
|
||||
The Command Center itself is lightweight — your hardware requirements depend on which tools and content you choose to install.
|
||||
|
||||
### Install N.O.M.A.D.
|
||||
|
||||
Open a terminal and run these two commands:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/master/install/install_nomad.sh -o install_nomad.sh
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo bash install_nomad.sh
|
||||
```
|
||||
|
||||
That's it. Once the install finishes, open a browser and go to:
|
||||
|
||||
- **Same machine:** `http://localhost:8080`
|
||||
- **Other devices on your network:** `http://YOUR_SERVER_IP:8080`
|
||||
|
||||
### About Internet & Privacy
|
||||
|
||||
N.O.M.A.D. is designed for offline use. Internet is only needed:
|
||||
- During initial installation
|
||||
- When downloading additional content
|
||||
|
||||
There is **zero telemetry** — your data stays on your device.
|
||||
|
||||
### About Security
|
||||
|
||||
N.O.M.A.D. has no built-in authentication — it's designed to be open and accessible. If you expose it on a network, consider using firewall rules to control which ports are accessible.
|
||||
|
||||
---
|
||||
|
||||
## After Installation
|
||||
|
||||
### 1. Run the Easy Setup Wizard
|
||||
|
||||
If this is your first time using N.O.M.A.D., the Easy Setup wizard will help you:
|
||||
- Choose which apps to enable
|
||||
- Download map regions for your area
|
||||
- Select knowledge collections (Wikipedia, medical references, etc.)
|
||||
|
||||
**[Launch Easy Setup →](/easy-setup)**
|
||||
|
||||
The wizard walks you through four simple steps:
|
||||
1. **Apps** — Choose additional tools like CyberChef or FlatNotes
|
||||
2. **Maps** — Select geographic regions for offline maps
|
||||
3. **ZIM Files** — Choose reference collections (Wikipedia, medical, survival guides)
|
||||
4. **Review** — Confirm your selections and start downloading
|
||||
|
||||
### 2. Wait for Downloads to Complete
|
||||
|
||||
Depending on what you selected, downloads may take a while. You can:
|
||||
- Monitor progress in the Settings area
|
||||
- Continue using features that are already installed
|
||||
- Leave your server running overnight for large downloads
|
||||
|
||||
### 3. Explore Your Content
|
||||
|
||||
Once downloads complete, you're ready to go. Your content works offline whenever you need it.
|
||||
|
||||
---
|
||||
|
||||
## Understanding Your Tools
|
||||
|
||||
### Kiwix — Your Offline Library
|
||||
|
||||
Kiwix stores compressed versions of websites and references that work without internet.
|
||||
|
||||
**What's included:**
|
||||
- Full Wikipedia (millions of articles)
|
||||
- Medical references and first aid guides
|
||||
- How-to guides and survival information
|
||||
- Classic books from Project Gutenberg
|
||||
|
||||
**How to use it:**
|
||||
1. Click **Kiwix** from the Command Center
|
||||
2. Choose a collection (like Wikipedia)
|
||||
3. Search or browse just like the regular website
|
||||
|
||||
**[Open Kiwix →](/kiwix)**
|
||||
|
||||
---
|
||||
|
||||
### Kolibri — Offline Education
|
||||
|
||||
Kolibri provides complete educational courses that work offline.
|
||||
|
||||
**What's included:**
|
||||
- Khan Academy video courses
|
||||
- Math, science, reading, and more
|
||||
- Progress tracking for learners
|
||||
- Works for all ages
|
||||
|
||||
**How to use it:**
|
||||
1. Click **Kolibri** from the Command Center
|
||||
2. Sign in or create a learner account
|
||||
3. Browse courses and start learning
|
||||
|
||||
**Tip:** Kolibri supports multiple users. Create accounts for each family member to track individual progress.
|
||||
|
||||
**[Open Kolibri →](/kolibri)**
|
||||
|
||||
---
|
||||
|
||||
### Open WebUI — Your AI Assistant
|
||||
|
||||
Chat with a local AI that runs entirely on your server — no internet needed.
|
||||
|
||||
**What can it do:**
|
||||
- Answer questions on any topic
|
||||
- Explain complex concepts simply
|
||||
- Help with writing and editing
|
||||
- Brainstorm ideas
|
||||
- Assist with problem-solving
|
||||
|
||||
**How to use it:**
|
||||
1. Click **Open WebUI** from the Command Center
|
||||
2. Type your question or request
|
||||
3. The AI responds in conversational style
|
||||
|
||||
**Tip:** Be specific in your questions. Instead of "tell me about plants," try "what vegetables grow well in shade?"
|
||||
|
||||
**[Open AI Chat →](/openwebui)**
|
||||
|
||||
---
|
||||
|
||||
### Maps — Offline Navigation
|
||||
|
||||
View maps without internet. Download the regions you need before going offline.
|
||||
|
||||
**How to use it:**
|
||||
1. Click **Maps** from the Command Center
|
||||
2. Navigate by dragging and zooming
|
||||
3. Search for locations using the search bar
|
||||
|
||||
**To add more map regions:**
|
||||
1. Go to **Settings → Maps Manager**
|
||||
2. Select the regions you need
|
||||
3. Click Download
|
||||
|
||||
**Tip:** Download maps for areas you travel to frequently, plus neighboring regions just in case.
|
||||
|
||||
**[Open Maps →](/maps)**
|
||||
|
||||
---
|
||||
|
||||
## Managing Your Server
|
||||
|
||||
### Adding More Content
|
||||
|
||||
As your needs change, you can add more content anytime:
|
||||
|
||||
- **More apps:** Settings → Apps
|
||||
- **More references:** Settings → ZIM Manager
|
||||
- **More map regions:** Settings → Maps Manager
|
||||
- **More educational content:** Through Kolibri's built-in content browser
|
||||
|
||||
### Keeping Things Updated
|
||||
|
||||
While you have internet, periodically check for updates:
|
||||
|
||||
1. Go to **Settings → Check for Updates**
|
||||
2. If updates are available, click to install
|
||||
3. Wait for the update to complete (your server will restart)
|
||||
|
||||
Content updates (Wikipedia, maps, etc.) can be managed separately from software updates.
|
||||
|
||||
### Monitoring System Health
|
||||
|
||||
Check on your server anytime:
|
||||
|
||||
1. Go to **Settings → System**
|
||||
2. View CPU, memory, and storage usage
|
||||
3. Check system uptime and status
|
||||
|
||||
---
|
||||
|
||||
## Tips for Best Results
|
||||
|
||||
### Before Going Offline
|
||||
|
||||
- **Update everything** — Run software and content updates
|
||||
- **Download what you need** — Maps, references, educational content
|
||||
- **Test it** — Make sure features work while you still have internet to troubleshoot
|
||||
|
||||
### Storage Management
|
||||
|
||||
Your server has limited storage. Prioritize:
|
||||
- Content you'll actually use
|
||||
- Critical references (medical, survival)
|
||||
- Maps for your region
|
||||
- Educational content matching your needs
|
||||
|
||||
Check storage usage in **Settings → System**.
|
||||
|
||||
### Getting Help
|
||||
|
||||
- **In-app docs:** You're reading them now
|
||||
- **AI assistant:** Ask Open WebUI for help with almost anything
|
||||
- **Release notes:** See what's new in each version
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
You're ready to use N.O.M.A.D. Here are some things to try:
|
||||
|
||||
1. **Look something up** — Search for a topic in Kiwix
|
||||
2. **Learn something** — Start a Khan Academy course in Kolibri
|
||||
3. **Ask a question** — Chat with the AI in Open WebUI
|
||||
4. **Explore maps** — Find your neighborhood in the Maps viewer
|
||||
|
||||
Enjoy your offline knowledge server!
|
||||
|
|
@ -1,71 +1,67 @@
|
|||
# Lorem Ipsum Markdown Showcase
|
||||
# Welcome to Project N.O.M.A.D.
|
||||
|
||||
Your personal offline knowledge server is ready to use.
|
||||
|
||||
## What is N.O.M.A.D.?
|
||||
|
||||
**N.O.M.A.D.** stands for **Node for Offline Media, Archives, and Data**. It's your personal server for accessing knowledge, education, and AI assistance — even when you have no internet connection.
|
||||
|
||||
Think of it as having Wikipedia, Khan Academy, an AI assistant, and offline maps all in one place, running on hardware you control.
|
||||
|
||||
## What Can You Do?
|
||||
|
||||
### Browse Offline Knowledge
|
||||
Access millions of Wikipedia articles, medical references, how-to guides, and ebooks — all stored locally on your server. No internet required.
|
||||
|
||||
**[Open Kiwix →](/kiwix)**
|
||||
|
||||
### Learn Something New
|
||||
Khan Academy courses covering math, science, economics, and more. Complete with videos and exercises, all available offline.
|
||||
|
||||
**[Open Kolibri →](/kolibri)**
|
||||
|
||||
### Chat with AI
|
||||
Ask questions, get explanations, brainstorm ideas, or get help with writing. Your local AI assistant works completely offline.
|
||||
|
||||
**[Open AI Chat →](/openwebui)**
|
||||
|
||||
### View Offline Maps
|
||||
Navigate and explore maps without an internet connection. Download regions you need before going offline.
|
||||
|
||||
**[Open Maps →](/maps)**
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
## Getting Started
|
||||
|
||||
This document serves as a comprehensive example of **Markdown's various formatting possibilities**, using the classic *Lorem Ipsum* text as its content. From basic text styling to lists, code blocks, and tables, you'll find a demonstration of common Markdown features here.
|
||||
**New to N.O.M.A.D.?** Use the Easy Setup wizard to configure your server and download content collections.
|
||||
|
||||
**[Run Easy Setup →](/easy-setup)**
|
||||
|
||||
Or explore the **[Getting Started Guide](/docs/getting-started)** for a walkthrough of all features.
|
||||
|
||||
---
|
||||
|
||||
## Basic Text Formatting
|
||||
## Quick Links
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
|
||||
* This text is **bold**.
|
||||
* This text is *italic*.
|
||||
* This text is ***bold and italic***.
|
||||
* This text is ~~struck through~~.
|
||||
* You can also use `backticks` for `inline code`.
|
||||
| I want to... | Go here |
|
||||
|--------------|---------|
|
||||
| Download more content | [Install Apps](/apps) |
|
||||
| Add Wikipedia/reference content | [ZIM Manager](/settings/zim-manager) |
|
||||
| Download map regions | [Maps Manager](/settings/maps-manager) |
|
||||
| Check for updates | [System Update](/settings/updates) |
|
||||
| View system status | [Settings](/settings) |
|
||||
|
||||
---
|
||||
|
||||
## Headers
|
||||
## Keeping Your Server Updated
|
||||
|
||||
Markdown supports up to six levels of headers.
|
||||
N.O.M.A.D. works best when kept up to date while you have internet access. This ensures you have the latest:
|
||||
- Software features and bug fixes
|
||||
- Wikipedia and reference content
|
||||
- Educational materials
|
||||
- AI model improvements
|
||||
|
||||
# Header 1
|
||||
## Header 2
|
||||
### Header 3
|
||||
#### Header 4
|
||||
##### Header 5
|
||||
###### Header 6
|
||||
When you go offline, you'll have everything you need — the last synced versions of all your content.
|
||||
|
||||
---
|
||||
|
||||
## Lists
|
||||
|
||||
### Unordered List
|
||||
|
||||
* Lorem ipsum dolor sit amet.
|
||||
* Consectetur adipiscing elit.
|
||||
* Sed do eiusmod tempor.
|
||||
* Incididunt ut labore et dolore magna.
|
||||
* Aliqua ut enim ad minim veniam.
|
||||
|
||||
### Ordered List
|
||||
|
||||
1. Quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
2. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
3. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
|
||||
---
|
||||
|
||||
## Blockquotes
|
||||
|
||||
> "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||
>
|
||||
> — John Doe, *Lorem Ipsum Anthology*
|
||||
|
||||
---
|
||||
|
||||
## Code Blocks
|
||||
|
||||
```python
|
||||
def fibonacci(n):
|
||||
a, b = 0, 1
|
||||
for i in range(n):
|
||||
print(a, end=" ")
|
||||
a, b = b, a + b
|
||||
|
||||
fibonacci(10)
|
||||
**[Check for Updates →](/settings/updates)**
|
||||
|
|
|
|||
190
admin/docs/use-cases.md
Normal file
190
admin/docs/use-cases.md
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
# What Can You Do With N.O.M.A.D.?
|
||||
|
||||
N.O.M.A.D. is designed to be your information lifeline when internet isn't available. Here's how different people use it.
|
||||
|
||||
---
|
||||
|
||||
## Emergency Preparedness
|
||||
|
||||
When disasters strike, internet and cell service often go down first. N.O.M.A.D. keeps critical information at your fingertips.
|
||||
|
||||
**What you can do:**
|
||||
- Look up first aid and emergency medical procedures
|
||||
- Access survival guides and emergency protocols
|
||||
- Find information about water purification, food storage, shelter building
|
||||
- Use offline maps to navigate when GPS services are degraded
|
||||
- Research plant identification, weather patterns, radio frequencies
|
||||
|
||||
**Recommended content:**
|
||||
- Medical Library ZIM collection
|
||||
- Survival/Prepper reference guides
|
||||
- Maps for your region and evacuation routes
|
||||
- Wikipedia (searchable for almost any topic)
|
||||
|
||||
---
|
||||
|
||||
## Homeschooling and Education
|
||||
|
||||
Teach your children anywhere, with or without internet. Complete curriculum available offline.
|
||||
|
||||
**What you can do:**
|
||||
- Access Khan Academy's full course library (math, science, reading, history)
|
||||
- Track progress for multiple students
|
||||
- Supplement with Wikipedia for research projects
|
||||
- Use the AI as a patient tutor for any subject
|
||||
- Access classic literature through Project Gutenberg
|
||||
|
||||
**Recommended content:**
|
||||
- Khan Academy courses via Kolibri
|
||||
- Wikipedia for Schools (curated for younger learners)
|
||||
- Project Gutenberg (classic books)
|
||||
- Educational ZIM collections
|
||||
|
||||
**Tip:** Create separate Kolibri accounts for each child to track their individual progress.
|
||||
|
||||
---
|
||||
|
||||
## Off-Grid Living
|
||||
|
||||
Living away from reliable internet doesn't mean living without information.
|
||||
|
||||
**What you can do:**
|
||||
- Research DIY projects and repairs
|
||||
- Look up gardening, animal husbandry, food preservation
|
||||
- Access medical references for remote healthcare
|
||||
- Learn new skills through educational videos
|
||||
- Get AI help with planning and problem-solving
|
||||
|
||||
**Recommended content:**
|
||||
- How-to and DIY reference collections
|
||||
- Medical and first aid guides
|
||||
- Agricultural and homesteading references
|
||||
- Maps for your rural area
|
||||
- Practical skills courses in Kolibri
|
||||
|
||||
---
|
||||
|
||||
## Remote Work Sites
|
||||
|
||||
Construction sites, research stations, ships, and remote facilities often lack reliable internet.
|
||||
|
||||
**What you can do:**
|
||||
- Access technical references and documentation
|
||||
- Use AI for writing assistance and analysis
|
||||
- Look up regulations, standards, and procedures
|
||||
- Provide educational resources for workers
|
||||
- Maintain communication records with note-taking apps
|
||||
|
||||
**Recommended content:**
|
||||
- Industry-specific technical references
|
||||
- Relevant Wikipedia categories
|
||||
- Maps of work areas
|
||||
- Documentation and compliance guides
|
||||
|
||||
---
|
||||
|
||||
## Travel and Expeditions
|
||||
|
||||
International travel, cruises, camping trips — stay informed anywhere.
|
||||
|
||||
**What you can do:**
|
||||
- Access maps without expensive roaming data
|
||||
- Research destinations, history, and culture
|
||||
- Translate concepts with AI assistance
|
||||
- Identify plants, animals, and geological features
|
||||
- Access travel health information
|
||||
|
||||
**Recommended content:**
|
||||
- Maps for destination countries/regions
|
||||
- Wikipedia in relevant languages
|
||||
- Medical/health references
|
||||
- Cultural and historical content
|
||||
|
||||
---
|
||||
|
||||
## Privacy-Conscious Users
|
||||
|
||||
Some people simply prefer to keep their searches and questions private.
|
||||
|
||||
**What you can do:**
|
||||
- Search Wikipedia without being tracked
|
||||
- Ask AI questions that stay on your own hardware
|
||||
- Learn about sensitive topics privately
|
||||
- Keep your intellectual curiosity to yourself
|
||||
|
||||
**How it works:**
|
||||
- All data stays on your server
|
||||
- No search history sent to companies
|
||||
- No AI conversations leave your network
|
||||
- You control your own information
|
||||
|
||||
---
|
||||
|
||||
## Medical Reference
|
||||
|
||||
When you can't reach a doctor, having reliable medical information can be critical.
|
||||
|
||||
**What you can access:**
|
||||
- NHS Medicines A-Z (drug information and interactions)
|
||||
- Medical Library (field medicine, emergency procedures)
|
||||
- First aid guides
|
||||
- Anatomy and physiology references
|
||||
- Disease and symptom information
|
||||
|
||||
**Important:** Medical references are for information only. They don't replace professional medical care. In emergencies, always seek professional help when possible.
|
||||
|
||||
**Recommended content:**
|
||||
- Medical Essentials ZIM collection
|
||||
- NHS Medicines reference
|
||||
- First aid and emergency medicine guides
|
||||
|
||||
---
|
||||
|
||||
## Academic Research
|
||||
|
||||
Students and researchers can work without depending on university networks.
|
||||
|
||||
**What you can do:**
|
||||
- Access Wikipedia's extensive article database
|
||||
- Use AI for research assistance and summarization
|
||||
- Work on papers and projects offline
|
||||
- Cross-reference multiple sources
|
||||
- Take notes with built-in tools
|
||||
|
||||
**Recommended content:**
|
||||
- Full Wikipedia
|
||||
- Academic and educational references
|
||||
- Subject-specific ZIM collections
|
||||
- Note-taking apps (FlatNotes)
|
||||
|
||||
---
|
||||
|
||||
## Setting Up for Your Use Case
|
||||
|
||||
### Step 1: Identify Your Needs
|
||||
What situations might you face without internet? What information would you need?
|
||||
|
||||
### Step 2: Prioritize Content
|
||||
Storage is limited. Focus on:
|
||||
1. Critical safety information (medical, emergency)
|
||||
2. Content matching your primary use case
|
||||
3. General reference (Wikipedia)
|
||||
4. Nice-to-have additions
|
||||
|
||||
### Step 3: Download While You Can
|
||||
Keep your server updated while you have internet. You never know when you'll need to go offline.
|
||||
|
||||
### Step 4: Practice
|
||||
Try using N.O.M.A.D. before you need it. Familiarity with the tools makes them more useful in a crisis.
|
||||
|
||||
---
|
||||
|
||||
## Need Something Specific?
|
||||
|
||||
N.O.M.A.D. content is customizable. If you don't see what you need:
|
||||
|
||||
1. **Check ZIM Remote Explorer** — Thousands of ZIM files are available
|
||||
2. **Browse Kolibri channels** — Educational content for many subjects
|
||||
3. **Request features** — Let us know what content would help you
|
||||
|
||||
Your offline server, your content choices.
|
||||
88
admin/inertia/components/CategoryCard.tsx
Normal file
88
admin/inertia/components/CategoryCard.tsx
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { formatBytes } from '~/lib/util'
|
||||
import DynamicIcon, { DynamicIconName } from './DynamicIcon'
|
||||
import { CuratedCategory, CategoryTier } from '../../types/downloads'
|
||||
import classNames from 'classnames'
|
||||
import { IconChevronRight, IconCircleCheck } from '@tabler/icons-react'
|
||||
|
||||
export interface CategoryCardProps {
|
||||
category: CuratedCategory
|
||||
selectedTier?: CategoryTier | null
|
||||
onClick?: (category: CuratedCategory) => void
|
||||
}
|
||||
|
||||
const CategoryCard: React.FC<CategoryCardProps> = ({ category, selectedTier, onClick }) => {
|
||||
// Calculate total size range across all tiers
|
||||
const getTierTotalSize = (tier: CategoryTier, allTiers: CategoryTier[]): number => {
|
||||
let total = tier.resources.reduce((acc, r) => acc + r.size_mb * 1024 * 1024, 0)
|
||||
|
||||
// Add included tier sizes recursively
|
||||
if (tier.includesTier) {
|
||||
const includedTier = allTiers.find(t => t.slug === tier.includesTier)
|
||||
if (includedTier) {
|
||||
total += getTierTotalSize(includedTier, allTiers)
|
||||
}
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
const minSize = getTierTotalSize(category.tiers[0], category.tiers)
|
||||
const maxSize = getTierTotalSize(category.tiers[category.tiers.length - 1], category.tiers)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'flex flex-col bg-desert-green rounded-lg p-6 text-white border shadow-sm hover:shadow-lg transition-shadow cursor-pointer h-80',
|
||||
selectedTier ? 'border-lime-400 border-2' : 'border-desert-green'
|
||||
)}
|
||||
onClick={() => onClick?.(category)}
|
||||
>
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="flex justify-between w-full items-center">
|
||||
<div className="flex items-center">
|
||||
<DynamicIcon icon={category.icon as DynamicIconName} className="w-6 h-6 mr-2" />
|
||||
<h3 className="text-lg font-semibold">{category.name}</h3>
|
||||
</div>
|
||||
{selectedTier ? (
|
||||
<div className="flex items-center">
|
||||
<IconCircleCheck className="w-5 h-5 text-lime-400" />
|
||||
<span className="text-lime-400 text-sm ml-1">{selectedTier.name}</span>
|
||||
</div>
|
||||
) : (
|
||||
<IconChevronRight className="w-5 h-5 text-white opacity-70" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-200 grow">{category.description}</p>
|
||||
|
||||
<div className="mt-4 pt-4 border-t border-white/20">
|
||||
<p className="text-sm text-gray-300 mb-2">
|
||||
{category.tiers.length} tiers available
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{category.tiers.map((tier) => (
|
||||
<span
|
||||
key={tier.slug}
|
||||
className={classNames(
|
||||
'text-xs px-2 py-1 rounded',
|
||||
tier.recommended
|
||||
? 'bg-lime-500/30 text-lime-200'
|
||||
: 'bg-white/10 text-gray-300',
|
||||
selectedTier?.slug === tier.slug && 'ring-2 ring-lime-400'
|
||||
)}
|
||||
>
|
||||
{tier.name}
|
||||
{tier.recommended && ' *'}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-gray-300 text-xs mt-3">
|
||||
Size: {formatBytes(minSize, 1)} - {formatBytes(maxSize, 1)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CategoryCard
|
||||
122
admin/inertia/components/StorageProjectionBar.tsx
Normal file
122
admin/inertia/components/StorageProjectionBar.tsx
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import classNames from '~/lib/classNames'
|
||||
import { formatBytes } from '~/lib/util'
|
||||
import { IconAlertTriangle, IconServer } from '@tabler/icons-react'
|
||||
|
||||
interface StorageProjectionBarProps {
|
||||
totalSize: number // Total disk size in bytes
|
||||
currentUsed: number // Currently used space in bytes
|
||||
projectedAddition: number // Additional space that will be used in bytes
|
||||
}
|
||||
|
||||
export default function StorageProjectionBar({
|
||||
totalSize,
|
||||
currentUsed,
|
||||
projectedAddition,
|
||||
}: StorageProjectionBarProps) {
|
||||
const projectedTotal = currentUsed + projectedAddition
|
||||
const currentPercent = (currentUsed / totalSize) * 100
|
||||
const projectedPercent = (projectedAddition / totalSize) * 100
|
||||
const projectedTotalPercent = (projectedTotal / totalSize) * 100
|
||||
const remainingAfter = totalSize - projectedTotal
|
||||
const willExceed = projectedTotal > totalSize
|
||||
|
||||
// Determine warning level based on projected total
|
||||
const getProjectedColor = () => {
|
||||
if (willExceed) return 'bg-desert-red'
|
||||
if (projectedTotalPercent >= 90) return 'bg-desert-orange'
|
||||
if (projectedTotalPercent >= 75) return 'bg-desert-tan'
|
||||
return 'bg-desert-olive'
|
||||
}
|
||||
|
||||
const getProjectedGlow = () => {
|
||||
if (willExceed) return 'shadow-desert-red/50'
|
||||
if (projectedTotalPercent >= 90) return 'shadow-desert-orange/50'
|
||||
if (projectedTotalPercent >= 75) return 'shadow-desert-tan/50'
|
||||
return 'shadow-desert-olive/50'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-desert-stone-lighter/30 rounded-lg p-4 border border-desert-stone-light">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<IconServer size={20} className="text-desert-green" />
|
||||
<span className="font-semibold text-desert-green">Storage</span>
|
||||
</div>
|
||||
<div className="text-sm text-desert-stone-dark font-mono">
|
||||
{formatBytes(projectedTotal, 1)} / {formatBytes(totalSize, 1)}
|
||||
{projectedAddition > 0 && (
|
||||
<span className="text-desert-stone ml-2">
|
||||
(+{formatBytes(projectedAddition, 1)} selected)
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress bar */}
|
||||
<div className="relative">
|
||||
<div className="h-8 bg-desert-green-lighter/20 rounded-lg border border-desert-stone-light overflow-hidden">
|
||||
{/* Current usage - darker/subdued */}
|
||||
<div
|
||||
className="absolute h-full bg-desert-stone transition-all duration-300"
|
||||
style={{ width: `${Math.min(currentPercent, 100)}%` }}
|
||||
/>
|
||||
{/* Projected addition - highlighted */}
|
||||
{projectedAddition > 0 && (
|
||||
<div
|
||||
className={classNames(
|
||||
'absolute h-full transition-all duration-300 shadow-lg',
|
||||
getProjectedColor(),
|
||||
getProjectedGlow()
|
||||
)}
|
||||
style={{
|
||||
left: `${Math.min(currentPercent, 100)}%`,
|
||||
width: `${Math.min(projectedPercent, 100 - currentPercent)}%`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Percentage label */}
|
||||
<div
|
||||
className={classNames(
|
||||
'absolute top-1/2 -translate-y-1/2 font-bold text-sm',
|
||||
projectedTotalPercent > 15
|
||||
? 'left-3 text-desert-white drop-shadow-md'
|
||||
: 'right-3 text-desert-green'
|
||||
)}
|
||||
>
|
||||
{Math.round(projectedTotalPercent)}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Legend and warnings */}
|
||||
<div className="flex items-center justify-between mt-3">
|
||||
<div className="flex items-center gap-4 text-xs">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded bg-desert-stone" />
|
||||
<span className="text-desert-stone-dark">Current ({formatBytes(currentUsed, 1)})</span>
|
||||
</div>
|
||||
{projectedAddition > 0 && (
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className={classNames('w-3 h-3 rounded', getProjectedColor())} />
|
||||
<span className="text-desert-stone-dark">
|
||||
Selected (+{formatBytes(projectedAddition, 1)})
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{willExceed ? (
|
||||
<div className="flex items-center gap-1.5 text-desert-red text-xs font-medium">
|
||||
<IconAlertTriangle size={14} />
|
||||
<span>Exceeds available space by {formatBytes(projectedTotal - totalSize, 1)}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-xs text-desert-stone">
|
||||
{formatBytes(remainingAfter, 1)} will remain free
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
205
admin/inertia/components/TierSelectionModal.tsx
Normal file
205
admin/inertia/components/TierSelectionModal.tsx
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
import { Fragment } from 'react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { IconX, IconCheck, IconInfoCircle } from '@tabler/icons-react'
|
||||
import { CuratedCategory, CategoryTier, CategoryResource } from '../../types/downloads'
|
||||
import { formatBytes } from '~/lib/util'
|
||||
import classNames from 'classnames'
|
||||
import DynamicIcon, { DynamicIconName } from './DynamicIcon'
|
||||
|
||||
interface TierSelectionModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
category: CuratedCategory | null
|
||||
selectedTierSlug?: string | null
|
||||
onSelectTier: (category: CuratedCategory, tier: CategoryTier) => void
|
||||
}
|
||||
|
||||
const TierSelectionModal: React.FC<TierSelectionModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
category,
|
||||
selectedTierSlug,
|
||||
onSelectTier,
|
||||
}) => {
|
||||
if (!category) return null
|
||||
|
||||
// Get all resources for a tier (including inherited resources)
|
||||
const getAllResourcesForTier = (tier: CategoryTier): CategoryResource[] => {
|
||||
const resources = [...tier.resources]
|
||||
|
||||
if (tier.includesTier) {
|
||||
const includedTier = category.tiers.find(t => t.slug === tier.includesTier)
|
||||
if (includedTier) {
|
||||
resources.unshift(...getAllResourcesForTier(includedTier))
|
||||
}
|
||||
}
|
||||
|
||||
return resources
|
||||
}
|
||||
|
||||
const getTierTotalSize = (tier: CategoryTier): number => {
|
||||
return getAllResourcesForTier(tier).reduce((acc, r) => acc + r.size_mb * 1024 * 1024, 0)
|
||||
}
|
||||
|
||||
return (
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-50" onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black/50" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="w-full max-w-4xl transform overflow-hidden rounded-lg bg-white shadow-xl transition-all">
|
||||
{/* Header */}
|
||||
<div className="bg-desert-green px-6 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<DynamicIcon
|
||||
icon={category.icon as DynamicIconName}
|
||||
className="w-8 h-8 text-white mr-3"
|
||||
/>
|
||||
<div>
|
||||
<Dialog.Title className="text-xl font-semibold text-white">
|
||||
{category.name}
|
||||
</Dialog.Title>
|
||||
<p className="text-sm text-gray-200">{category.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-white/70 hover:text-white transition-colors"
|
||||
>
|
||||
<IconX size={24} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-6">
|
||||
<p className="text-gray-600 mb-6">
|
||||
Select a tier based on your storage capacity and needs. Higher tiers include all content from lower tiers.
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
{category.tiers.map((tier, index) => {
|
||||
const allResources = getAllResourcesForTier(tier)
|
||||
const totalSize = getTierTotalSize(tier)
|
||||
const isSelected = selectedTierSlug === tier.slug
|
||||
|
||||
return (
|
||||
<div
|
||||
key={tier.slug}
|
||||
onClick={() => onSelectTier(category, tier)}
|
||||
className={classNames(
|
||||
'border-2 rounded-lg p-5 cursor-pointer transition-all',
|
||||
isSelected
|
||||
? 'border-desert-green bg-desert-green/5 shadow-md'
|
||||
: 'border-gray-200 hover:border-desert-green/50 hover:shadow-sm',
|
||||
tier.recommended && !isSelected && 'border-lime-500/50'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
{tier.name}
|
||||
</h3>
|
||||
{tier.recommended && (
|
||||
<span className="text-xs bg-lime-500 text-white px-2 py-0.5 rounded">
|
||||
Recommended
|
||||
</span>
|
||||
)}
|
||||
{tier.includesTier && (
|
||||
<span className="text-xs text-gray-500">
|
||||
(includes {category.tiers.find(t => t.slug === tier.includesTier)?.name})
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-gray-600 text-sm mb-3">{tier.description}</p>
|
||||
|
||||
{/* Resources preview */}
|
||||
<div className="bg-gray-50 rounded p-3">
|
||||
<p className="text-xs text-gray-500 mb-2 font-medium">
|
||||
{allResources.length} resources included:
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||
{allResources.map((resource, idx) => (
|
||||
<div key={idx} className="flex items-start text-sm">
|
||||
<IconCheck size={14} className="text-desert-green mr-1.5 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<span className="text-gray-700">{resource.title}</span>
|
||||
<span className="text-gray-400 text-xs ml-1">
|
||||
({formatBytes(resource.size_mb * 1024 * 1024, 0)})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ml-4 text-right flex-shrink-0">
|
||||
<div className="text-lg font-semibold text-gray-900">
|
||||
{formatBytes(totalSize, 1)}
|
||||
</div>
|
||||
<div className={classNames(
|
||||
'w-6 h-6 rounded-full border-2 flex items-center justify-center mt-2 ml-auto',
|
||||
isSelected
|
||||
? 'border-desert-green bg-desert-green'
|
||||
: 'border-gray-300'
|
||||
)}>
|
||||
{isSelected && <IconCheck size={16} className="text-white" />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Info note */}
|
||||
<div className="mt-6 flex items-start gap-2 text-sm text-gray-500 bg-blue-50 p-3 rounded">
|
||||
<IconInfoCircle size={18} className="text-blue-500 flex-shrink-0 mt-0.5" />
|
||||
<p>
|
||||
You can change your selection at any time. Downloads will begin when you complete the setup wizard.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="bg-gray-50 px-6 py-4 flex justify-end gap-3">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-gray-700 hover:text-gray-900 transition-colors"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
)
|
||||
}
|
||||
|
||||
export default TierSelectionModal
|
||||
|
|
@ -1,17 +1,22 @@
|
|||
import { Head, router } from '@inertiajs/react'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useState, useMemo } from 'react'
|
||||
import AppLayout from '~/layouts/AppLayout'
|
||||
import StyledButton from '~/components/StyledButton'
|
||||
import api from '~/lib/api'
|
||||
import { ServiceSlim } from '../../../types/services'
|
||||
import CuratedCollectionCard from '~/components/CuratedCollectionCard'
|
||||
import CategoryCard from '~/components/CategoryCard'
|
||||
import TierSelectionModal from '~/components/TierSelectionModal'
|
||||
import LoadingSpinner from '~/components/LoadingSpinner'
|
||||
import Alert from '~/components/Alert'
|
||||
import { IconCheck, IconChevronDown, IconChevronUp } from '@tabler/icons-react'
|
||||
import StorageProjectionBar from '~/components/StorageProjectionBar'
|
||||
import { useNotifications } from '~/context/NotificationContext'
|
||||
import useInternetStatus from '~/hooks/useInternetStatus'
|
||||
import { useSystemInfo } from '~/hooks/useSystemInfo'
|
||||
import classNames from 'classnames'
|
||||
import { CuratedCategory, CategoryTier, CategoryResource } from '../../../types/downloads'
|
||||
|
||||
// Capability definitions - maps user-friendly categories to services
|
||||
interface Capability {
|
||||
|
|
@ -102,6 +107,21 @@ type WizardStep = 1 | 2 | 3 | 4
|
|||
|
||||
const CURATED_MAP_COLLECTIONS_KEY = 'curated-map-collections'
|
||||
const CURATED_ZIM_COLLECTIONS_KEY = 'curated-zim-collections'
|
||||
const CURATED_CATEGORIES_KEY = 'curated-categories'
|
||||
const CATEGORIES_URL =
|
||||
'https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/feature/tiered-collections/collections/kiwix-categories.json'
|
||||
|
||||
// Helper to get all resources for a tier (including inherited resources)
|
||||
const getAllResourcesForTier = (tier: CategoryTier, allTiers: CategoryTier[]): CategoryResource[] => {
|
||||
const resources = [...tier.resources]
|
||||
if (tier.includesTier) {
|
||||
const includedTier = allTiers.find((t) => t.slug === tier.includesTier)
|
||||
if (includedTier) {
|
||||
resources.unshift(...getAllResourcesForTier(includedTier, allTiers))
|
||||
}
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
export default function EasySetupWizard(props: { system: { services: ServiceSlim[] } }) {
|
||||
const [currentStep, setCurrentStep] = useState<WizardStep>(1)
|
||||
|
|
@ -111,14 +131,21 @@ export default function EasySetupWizard(props: { system: { services: ServiceSlim
|
|||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
const [showAdditionalTools, setShowAdditionalTools] = useState(false)
|
||||
|
||||
// Category/tier selection state
|
||||
const [selectedTiers, setSelectedTiers] = useState<Map<string, CategoryTier>>(new Map())
|
||||
const [tierModalOpen, setTierModalOpen] = useState(false)
|
||||
const [activeCategory, setActiveCategory] = useState<CuratedCategory | null>(null)
|
||||
|
||||
const { addNotification } = useNotifications()
|
||||
const { isOnline } = useInternetStatus()
|
||||
const queryClient = useQueryClient()
|
||||
const { data: systemInfo } = useSystemInfo({ enabled: true })
|
||||
|
||||
const anySelectionMade =
|
||||
selectedServices.length > 0 ||
|
||||
selectedMapCollections.length > 0 ||
|
||||
selectedZimCollections.length > 0
|
||||
selectedZimCollections.length > 0 ||
|
||||
selectedTiers.size > 0
|
||||
|
||||
const { data: mapCollections, isLoading: isLoadingMaps } = useQuery({
|
||||
queryKey: [CURATED_MAP_COLLECTIONS_KEY],
|
||||
|
|
@ -136,6 +163,20 @@ export default function EasySetupWizard(props: { system: { services: ServiceSlim
|
|||
const allServices = props.system.services
|
||||
|
||||
// Services that can still be installed (not already installed)
|
||||
// Fetch curated categories with tiers
|
||||
const { data: categories, isLoading: isLoadingCategories } = useQuery({
|
||||
queryKey: [CURATED_CATEGORIES_KEY],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(CATEGORIES_URL)
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch categories')
|
||||
}
|
||||
const data = await response.json()
|
||||
return data.categories as CuratedCategory[]
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
})
|
||||
|
||||
const availableServices = props.system.services.filter(
|
||||
(service) => !service.installed && service.installation_status !== 'installing'
|
||||
)
|
||||
|
|
@ -163,6 +204,85 @@ export default function EasySetupWizard(props: { system: { services: ServiceSlim
|
|||
)
|
||||
}
|
||||
|
||||
// Category/tier handlers
|
||||
const handleCategoryClick = (category: CuratedCategory) => {
|
||||
if (!isOnline) return
|
||||
setActiveCategory(category)
|
||||
setTierModalOpen(true)
|
||||
}
|
||||
|
||||
const handleTierSelect = (category: CuratedCategory, tier: CategoryTier) => {
|
||||
setSelectedTiers((prev) => {
|
||||
const newMap = new Map(prev)
|
||||
// If same tier is selected, deselect it
|
||||
if (prev.get(category.slug)?.slug === tier.slug) {
|
||||
newMap.delete(category.slug)
|
||||
} else {
|
||||
newMap.set(category.slug, tier)
|
||||
}
|
||||
return newMap
|
||||
})
|
||||
}
|
||||
|
||||
const closeTierModal = () => {
|
||||
setTierModalOpen(false)
|
||||
setActiveCategory(null)
|
||||
}
|
||||
|
||||
// Get all resources from selected tiers for downloading
|
||||
const getSelectedTierResources = (): CategoryResource[] => {
|
||||
if (!categories) return []
|
||||
const resources: CategoryResource[] = []
|
||||
selectedTiers.forEach((tier, categorySlug) => {
|
||||
const category = categories.find((c) => c.slug === categorySlug)
|
||||
if (category) {
|
||||
resources.push(...getAllResourcesForTier(tier, category.tiers))
|
||||
}
|
||||
})
|
||||
return resources
|
||||
}
|
||||
|
||||
// Calculate total projected storage from all selections
|
||||
const projectedStorageBytes = useMemo(() => {
|
||||
let totalBytes = 0
|
||||
|
||||
// Add tier resources
|
||||
const tierResources = getSelectedTierResources()
|
||||
totalBytes += tierResources.reduce((sum, r) => sum + r.size_mb * 1024 * 1024, 0)
|
||||
|
||||
// Add map collections
|
||||
if (mapCollections) {
|
||||
selectedMapCollections.forEach((slug) => {
|
||||
const collection = mapCollections.find((c) => c.slug === slug)
|
||||
if (collection) {
|
||||
totalBytes += collection.resources.reduce((sum, r) => sum + r.size_mb * 1024 * 1024, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Add ZIM collections
|
||||
if (zimCollections) {
|
||||
selectedZimCollections.forEach((slug) => {
|
||||
const collection = zimCollections.find((c) => c.slug === slug)
|
||||
if (collection) {
|
||||
totalBytes += collection.resources.reduce((sum, r) => sum + r.size_mb * 1024 * 1024, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return totalBytes
|
||||
}, [selectedTiers, selectedMapCollections, selectedZimCollections, categories, mapCollections, zimCollections])
|
||||
|
||||
// Get primary disk/filesystem info for storage projection
|
||||
// Try disk array first (Linux/production), fall back to fsSize (Windows/dev)
|
||||
const primaryDisk = systemInfo?.disk?.[0]
|
||||
const primaryFs = systemInfo?.fsSize?.[0]
|
||||
const storageInfo = primaryDisk
|
||||
? { totalSize: primaryDisk.totalSize, totalUsed: primaryDisk.totalUsed }
|
||||
: primaryFs
|
||||
? { totalSize: primaryFs.size, totalUsed: primaryFs.used }
|
||||
: null
|
||||
|
||||
const canProceedToNextStep = () => {
|
||||
if (!isOnline) return false // Must be online to proceed
|
||||
if (currentStep === 1) return true // Can skip app installation
|
||||
|
|
@ -200,9 +320,12 @@ export default function EasySetupWizard(props: { system: { services: ServiceSlim
|
|||
|
||||
await Promise.all(installPromises)
|
||||
|
||||
// Download collections and individual tier resources
|
||||
const tierResources = getSelectedTierResources()
|
||||
const downloadPromises = [
|
||||
...selectedMapCollections.map((slug) => api.downloadMapCollection(slug)),
|
||||
...selectedZimCollections.map((slug) => api.downloadZimCollection(slug)),
|
||||
...tierResources.map((resource) => api.downloadRemoteZimFile(resource.url)),
|
||||
]
|
||||
|
||||
await Promise.all(downloadPromises)
|
||||
|
|
@ -263,7 +386,7 @@ export default function EasySetupWizard(props: { system: { services: ServiceSlim
|
|||
const steps = [
|
||||
{ number: 1, label: 'Apps' },
|
||||
{ number: 2, label: 'Maps' },
|
||||
{ number: 3, label: 'ZIM Files' },
|
||||
{ number: 3, label: 'Content' },
|
||||
{ number: 4, label: 'Review' },
|
||||
]
|
||||
|
||||
|
|
@ -577,44 +700,79 @@ export default function EasySetupWizard(props: { system: { services: ServiceSlim
|
|||
const renderStep3 = () => (
|
||||
<div className="space-y-6">
|
||||
<div className="text-center mb-6">
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-2">Choose ZIM Files</h2>
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-2">Choose Content Collections</h2>
|
||||
<p className="text-gray-600">
|
||||
Select ZIM file collections for offline knowledge. You can always download more later.
|
||||
Select content categories for offline knowledge. Click a category to choose your preferred tier based on storage capacity.
|
||||
</p>
|
||||
</div>
|
||||
{isLoadingZims ? (
|
||||
|
||||
{/* Curated Categories with Tiers */}
|
||||
{isLoadingCategories ? (
|
||||
<div className="flex justify-center py-12">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
) : zimCollections && zimCollections.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{zimCollections.map((collection) => (
|
||||
<div
|
||||
key={collection.slug}
|
||||
onClick={() =>
|
||||
isOnline && !collection.all_downloaded && toggleZimCollection(collection.slug)
|
||||
}
|
||||
className={classNames(
|
||||
'relative',
|
||||
selectedZimCollections.includes(collection.slug) &&
|
||||
'ring-4 ring-desert-green rounded-lg',
|
||||
collection.all_downloaded && 'opacity-75',
|
||||
!isOnline && 'opacity-50 cursor-not-allowed'
|
||||
)}
|
||||
>
|
||||
<CuratedCollectionCard collection={collection} size="large" />
|
||||
{selectedZimCollections.includes(collection.slug) && (
|
||||
<div className="absolute top-2 right-2 bg-desert-green rounded-full p-1">
|
||||
<IconCheck size={32} className="text-white" />
|
||||
</div>
|
||||
)}
|
||||
) : categories && categories.length > 0 ? (
|
||||
<>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{categories.map((category) => (
|
||||
<CategoryCard
|
||||
key={category.slug}
|
||||
category={category}
|
||||
selectedTier={selectedTiers.get(category.slug) || null}
|
||||
onClick={handleCategoryClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Tier Selection Modal */}
|
||||
<TierSelectionModal
|
||||
isOpen={tierModalOpen}
|
||||
onClose={closeTierModal}
|
||||
category={activeCategory}
|
||||
selectedTierSlug={activeCategory ? selectedTiers.get(activeCategory.slug)?.slug : null}
|
||||
onSelectTier={handleTierSelect}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{/* Legacy flat collections - show if available and no categories */}
|
||||
{(!categories || categories.length === 0) && (
|
||||
<>
|
||||
{isLoadingZims ? (
|
||||
<div className="flex justify-center py-12">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-gray-600 text-lg">No ZIM collections available at this time.</p>
|
||||
</div>
|
||||
) : zimCollections && zimCollections.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{zimCollections.map((collection) => (
|
||||
<div
|
||||
key={collection.slug}
|
||||
onClick={() =>
|
||||
isOnline && !collection.all_downloaded && toggleZimCollection(collection.slug)
|
||||
}
|
||||
className={classNames(
|
||||
'relative',
|
||||
selectedZimCollections.includes(collection.slug) &&
|
||||
'ring-4 ring-desert-green rounded-lg',
|
||||
collection.all_downloaded && 'opacity-75',
|
||||
!isOnline && 'opacity-50 cursor-not-allowed'
|
||||
)}
|
||||
>
|
||||
<CuratedCollectionCard collection={collection} size="large" />
|
||||
{selectedZimCollections.includes(collection.slug) && (
|
||||
<div className="absolute top-2 right-2 bg-desert-green rounded-full p-1">
|
||||
<IconCheck size={32} className="text-white" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-gray-600 text-lg">No content collections available at this time.</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
|
@ -623,7 +781,8 @@ export default function EasySetupWizard(props: { system: { services: ServiceSlim
|
|||
const hasSelections =
|
||||
selectedServices.length > 0 ||
|
||||
selectedMapCollections.length > 0 ||
|
||||
selectedZimCollections.length > 0
|
||||
selectedZimCollections.length > 0 ||
|
||||
selectedTiers.size > 0
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
|
|
@ -700,6 +859,39 @@ export default function EasySetupWizard(props: { system: { services: ServiceSlim
|
|||
</div>
|
||||
)}
|
||||
|
||||
{selectedTiers.size > 0 && (
|
||||
<div className="bg-white rounded-lg border-2 border-desert-stone-light p-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-4">
|
||||
Content Categories ({selectedTiers.size})
|
||||
</h3>
|
||||
{Array.from(selectedTiers.entries()).map(([categorySlug, tier]) => {
|
||||
const category = categories?.find((c) => c.slug === categorySlug)
|
||||
if (!category) return null
|
||||
const resources = getAllResourcesForTier(tier, category.tiers)
|
||||
return (
|
||||
<div key={categorySlug} className="mb-4 last:mb-0">
|
||||
<div className="flex items-center mb-2">
|
||||
<IconCheck size={20} className="text-desert-green mr-2" />
|
||||
<span className="text-gray-900 font-medium">
|
||||
{category.name} - {tier.name}
|
||||
</span>
|
||||
<span className="text-gray-500 text-sm ml-2">
|
||||
({resources.length} files)
|
||||
</span>
|
||||
</div>
|
||||
<ul className="ml-7 space-y-1">
|
||||
{resources.map((resource, idx) => (
|
||||
<li key={idx} className="text-sm text-gray-600">
|
||||
{resource.title}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Alert
|
||||
title="Ready to Start"
|
||||
message="Click 'Complete Setup' to begin installing apps and downloading content. This may take some time depending on your internet connection and the size of the downloads."
|
||||
|
|
@ -727,6 +919,15 @@ export default function EasySetupWizard(props: { system: { services: ServiceSlim
|
|||
<div className="max-w-7xl mx-auto px-4 py-8">
|
||||
<div className="bg-white rounded-md shadow-md">
|
||||
{renderStepIndicator()}
|
||||
{storageInfo && (
|
||||
<div className="px-6 pt-4">
|
||||
<StorageProjectionBar
|
||||
totalSize={storageInfo.totalSize}
|
||||
currentUsed={storageInfo.totalUsed}
|
||||
projectedAddition={projectedStorageBytes}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="p-6 min-h-fit">
|
||||
{currentStep === 1 && renderStep1()}
|
||||
{currentStep === 2 && renderStep2()}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const NotificationsProvider = ({ children }: { children: React.ReactNode }) => {
|
|||
}
|
||||
|
||||
const removeNotification = (id: string) => {
|
||||
setNotifications(notifications.filter((n) => n.id !== id))
|
||||
setNotifications((prev) => prev.filter((n) => n.id !== id))
|
||||
}
|
||||
|
||||
const removeAllNotifications = () => {
|
||||
|
|
|
|||
|
|
@ -59,3 +59,33 @@ export type DownloadJobWithProgress = {
|
|||
filepath: string
|
||||
filetype: string
|
||||
}
|
||||
|
||||
// Tiered category types for curated collections UI
|
||||
export type CategoryResource = {
|
||||
title: string
|
||||
description: string
|
||||
size_mb: number
|
||||
url: string
|
||||
}
|
||||
|
||||
export type CategoryTier = {
|
||||
name: string
|
||||
slug: string
|
||||
description: string
|
||||
recommended?: boolean
|
||||
includesTier?: string
|
||||
resources: CategoryResource[]
|
||||
}
|
||||
|
||||
export type CuratedCategory = {
|
||||
name: string
|
||||
slug: string
|
||||
icon: string
|
||||
description: string
|
||||
language: string
|
||||
tiers: CategoryTier[]
|
||||
}
|
||||
|
||||
export type CuratedCategoriesFile = {
|
||||
categories: CuratedCategory[]
|
||||
}
|
||||
|
|
|
|||
107
collections/CATEGORIES-TODO.md
Normal file
107
collections/CATEGORIES-TODO.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# Kiwix Categories To-Do List
|
||||
|
||||
Potential categories to add to the tiered collections system in `kiwix-categories.json`.
|
||||
|
||||
## Current Categories (Completed)
|
||||
- [x] Medicine - Medical references, first aid, emergency care
|
||||
- [x] Survival & Preparedness - Food prep, prepper videos, repair guides
|
||||
- [x] Education & Reference - Wikipedia, textbooks, TED talks
|
||||
|
||||
---
|
||||
|
||||
## High Priority
|
||||
|
||||
### Technology & Programming
|
||||
Stack Overflow, developer documentation, coding tutorials
|
||||
- Stack Overflow (multiple tags available)
|
||||
- DevDocs documentation
|
||||
- freeCodeCamp
|
||||
- Programming language references
|
||||
|
||||
### Children & Family
|
||||
Age-appropriate educational content for kids
|
||||
- Wikipedia for Schools
|
||||
- Wikibooks Children's Bookshelf
|
||||
- Khan Academy Kids (via Kolibri - separate system)
|
||||
- Storybooks, fairy tales
|
||||
|
||||
### Trades & Vocational
|
||||
Practical skills for building, fixing, and maintaining
|
||||
- Electrical wiring guides
|
||||
- Plumbing basics
|
||||
- Automotive repair
|
||||
- Woodworking
|
||||
- Welding fundamentals
|
||||
|
||||
### Agriculture & Gardening
|
||||
Food production and farming (expand beyond what's in Survival)
|
||||
- Practical Plants database
|
||||
- Permaculture guides
|
||||
- Seed saving
|
||||
- Animal husbandry
|
||||
- Composting and soil management
|
||||
|
||||
---
|
||||
|
||||
## Medium Priority
|
||||
|
||||
### Languages & Reference
|
||||
Dictionaries, language learning, translation
|
||||
- Wiktionary (multiple languages)
|
||||
- Language learning resources
|
||||
- Translation dictionaries
|
||||
- Grammar guides
|
||||
|
||||
### History & Culture
|
||||
Historical knowledge and cultural encyclopedias
|
||||
- Wikipedia History portal content
|
||||
- Historical documents
|
||||
- Cultural archives
|
||||
- Biographies
|
||||
|
||||
### Legal & Civic
|
||||
Laws, rights, and civic procedures
|
||||
- Legal references
|
||||
- Constitutional documents
|
||||
- Civic procedures
|
||||
- Rights and responsibilities
|
||||
|
||||
### Communications
|
||||
Emergency and amateur radio, networking
|
||||
- Ham radio guides
|
||||
- Emergency communication protocols
|
||||
- Basic networking/IT
|
||||
- Signal procedures
|
||||
|
||||
---
|
||||
|
||||
## Nice To Have
|
||||
|
||||
### Entertainment
|
||||
Recreational reading and activities
|
||||
- Project Gutenberg (fiction categories)
|
||||
- Chess tutorials
|
||||
- Puzzles and games
|
||||
- Music theory
|
||||
|
||||
### Religion & Philosophy
|
||||
Spiritual and philosophical texts
|
||||
- Religious texts (various traditions)
|
||||
- Philosophy references
|
||||
- Ethics guides
|
||||
|
||||
### Regional/Non-English Bundles
|
||||
Content in other languages
|
||||
- Spanish language bundle
|
||||
- French language bundle
|
||||
- Other major languages
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Each category should have 3 tiers: Essential, Standard, Comprehensive
|
||||
- Higher tiers include all content from lower tiers via `includesTier`
|
||||
- Check Kiwix catalog for available ZIM files: https://download.kiwix.org/zim/
|
||||
- Consider storage constraints - Essential tiers should be <500MB ideally
|
||||
- Mark one tier as `recommended: true` (usually Essential)
|
||||
325
collections/kiwix-categories.json
Normal file
325
collections/kiwix-categories.json
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
{
|
||||
"categories": [
|
||||
{
|
||||
"name": "Medicine",
|
||||
"slug": "medicine",
|
||||
"icon": "IconStethoscope",
|
||||
"description": "Medical references, guides, and encyclopedias for healthcare information and emergency preparedness.",
|
||||
"language": "en",
|
||||
"tiers": [
|
||||
{
|
||||
"name": "Essential",
|
||||
"slug": "medicine-essential",
|
||||
"description": "Core medical references for first aid, medications, and emergency care. Start here.",
|
||||
"recommended": true,
|
||||
"resources": [
|
||||
{
|
||||
"title": "Medical Library",
|
||||
"description": "Field and emergency medicine books and guides",
|
||||
"url": "https://download.kiwix.org/zim/other/zimgit-medicine_en_2024-08.zim",
|
||||
"size_mb": 67
|
||||
},
|
||||
{
|
||||
"title": "NHS Medicines A to Z",
|
||||
"description": "How medicines work, dosages, side effects, and interactions",
|
||||
"url": "https://download.kiwix.org/zim/zimit/nhs.uk_en_medicines_2025-12.zim",
|
||||
"size_mb": 16
|
||||
},
|
||||
{
|
||||
"title": "Military Medicine",
|
||||
"description": "Tactical and field medicine manuals",
|
||||
"url": "https://download.kiwix.org/zim/zimit/fas-military-medicine_en_2025-06.zim",
|
||||
"size_mb": 78
|
||||
},
|
||||
{
|
||||
"title": "CDC Health Information",
|
||||
"description": "Disease prevention, travel health, and outbreak information",
|
||||
"url": "https://download.kiwix.org/zim/zimit/wwwnc.cdc.gov_en_all_2024-11.zim",
|
||||
"size_mb": 170
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Standard",
|
||||
"slug": "medicine-standard",
|
||||
"description": "Comprehensive medical encyclopedia with detailed health information. Includes everything in Essential.",
|
||||
"includesTier": "medicine-essential",
|
||||
"resources": [
|
||||
{
|
||||
"title": "MedlinePlus",
|
||||
"description": "NIH's consumer health encyclopedia - diseases, conditions, drugs, supplements",
|
||||
"url": "https://download.kiwix.org/zim/zimit/medlineplus.gov_en_all_2025-01.zim",
|
||||
"size_mb": 1800
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Comprehensive",
|
||||
"slug": "medicine-comprehensive",
|
||||
"description": "Professional-level medical references and textbooks. Includes everything in Standard.",
|
||||
"includesTier": "medicine-standard",
|
||||
"resources": [
|
||||
{
|
||||
"title": "Wikipedia Medicine",
|
||||
"description": "Curated medical articles from Wikipedia with images",
|
||||
"url": "https://download.kiwix.org/zim/wikipedia/wikipedia_en_medicine_maxi_2026-01.zim",
|
||||
"size_mb": 2000
|
||||
},
|
||||
{
|
||||
"title": "LibreTexts Medicine",
|
||||
"description": "Open-source medical textbooks and educational content",
|
||||
"url": "https://download.kiwix.org/zim/libretexts/libretexts.org_en_med_2025-01.zim",
|
||||
"size_mb": 1100
|
||||
},
|
||||
{
|
||||
"title": "LibrePathology",
|
||||
"description": "Pathology reference for disease identification",
|
||||
"url": "https://download.kiwix.org/zim/other/librepathology_en_all_maxi_2025-09.zim",
|
||||
"size_mb": 76
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Survival & Preparedness",
|
||||
"slug": "survival",
|
||||
"icon": "IconShieldCheck",
|
||||
"description": "Emergency preparedness, self-sufficiency, food storage, and practical survival skills.",
|
||||
"language": "en",
|
||||
"tiers": [
|
||||
{
|
||||
"name": "Essential",
|
||||
"slug": "survival-essential",
|
||||
"description": "Core food preparation and cooking guides. Lightweight text-based resources to get started.",
|
||||
"recommended": true,
|
||||
"resources": [
|
||||
{
|
||||
"title": "Food for Preppers",
|
||||
"description": "Recipes and techniques for food preservation and preparation",
|
||||
"url": "https://download.kiwix.org/zim/other/zimgit-food-preparation_en_2025-04.zim",
|
||||
"size_mb": 98
|
||||
},
|
||||
{
|
||||
"title": "FOSS Cooking",
|
||||
"description": "Quick and easy cooking guides and recipes",
|
||||
"url": "https://download.kiwix.org/zim/zimit/foss.cooking_en_all_2025-11.zim",
|
||||
"size_mb": 24
|
||||
},
|
||||
{
|
||||
"title": "Based.Cooking",
|
||||
"description": "Simple, practical recipes from the community",
|
||||
"url": "https://download.kiwix.org/zim/zimit/based.cooking_en_all_2025-11.zim",
|
||||
"size_mb": 16
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Standard",
|
||||
"slug": "survival-standard",
|
||||
"description": "Video guides for winter survival, bug-out gear, plus gardening and cooking Q&A. Includes Essential.",
|
||||
"includesTier": "survival-essential",
|
||||
"resources": [
|
||||
{
|
||||
"title": "Canadian Prepper: Winter Prepping",
|
||||
"description": "Video guides for winter survival and cold weather emergencies",
|
||||
"url": "https://download.kiwix.org/zim/videos/canadian_prepper_winterprepping_en_2025-11.zim",
|
||||
"size_mb": 1340
|
||||
},
|
||||
{
|
||||
"title": "Canadian Prepper: Bug Out Roll",
|
||||
"description": "Essential gear selection for your bug-out bag",
|
||||
"url": "https://download.kiwix.org/zim/videos/canadian_prepper_bugoutroll_en_2025-08.zim",
|
||||
"size_mb": 975
|
||||
},
|
||||
{
|
||||
"title": "Gardening Q&A",
|
||||
"description": "Stack Exchange Q&A for growing your own food",
|
||||
"url": "https://download.kiwix.org/zim/stack_exchange/gardening.stackexchange.com_en_all_2025-12.zim",
|
||||
"size_mb": 923
|
||||
},
|
||||
{
|
||||
"title": "Cooking Q&A",
|
||||
"description": "Stack Exchange Q&A for cooking techniques and food safety",
|
||||
"url": "https://download.kiwix.org/zim/stack_exchange/cooking.stackexchange.com_en_all_2025-12.zim",
|
||||
"size_mb": 236
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Comprehensive",
|
||||
"slug": "survival-comprehensive",
|
||||
"description": "Full prepper video library plus repair guides and DIY skills. Includes Standard.",
|
||||
"includesTier": "survival-standard",
|
||||
"resources": [
|
||||
{
|
||||
"title": "Urban Prepper",
|
||||
"description": "Comprehensive urban emergency preparedness video series",
|
||||
"url": "https://download.kiwix.org/zim/videos/urban-prepper_en_all_2025-11.zim",
|
||||
"size_mb": 2240
|
||||
},
|
||||
{
|
||||
"title": "Canadian Prepper: Prepping Food",
|
||||
"description": "Long-term food storage and survival meal preparation",
|
||||
"url": "https://download.kiwix.org/zim/videos/canadian_prepper_preppingfood_en_2025-09.zim",
|
||||
"size_mb": 2160
|
||||
},
|
||||
{
|
||||
"title": "Canadian Prepper: Bug Out Concepts",
|
||||
"description": "Strategies and planning for emergency evacuation",
|
||||
"url": "https://download.kiwix.org/zim/videos/canadian_prepper_bugoutconcepts_en_2025-11.zim",
|
||||
"size_mb": 2890
|
||||
},
|
||||
{
|
||||
"title": "Learning Self-Reliance",
|
||||
"description": "Prepping, survival skills, beekeeping, and homesteading",
|
||||
"url": "https://download.kiwix.org/zim/videos/lrnselfreliance_en_all_2025-12.zim",
|
||||
"size_mb": 3970
|
||||
},
|
||||
{
|
||||
"title": "iFixit Repair Guides",
|
||||
"description": "Step-by-step repair guides for electronics, appliances, and vehicles",
|
||||
"url": "https://download.kiwix.org/zim/ifixit/ifixit_en_all_2025-12.zim",
|
||||
"size_mb": 3570
|
||||
},
|
||||
{
|
||||
"title": "DIY & Home Improvement Q&A",
|
||||
"description": "Stack Exchange Q&A for home repairs and construction",
|
||||
"url": "https://download.kiwix.org/zim/stack_exchange/diy.stackexchange.com_en_all_2025-12.zim",
|
||||
"size_mb": 1900
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Education & Reference",
|
||||
"slug": "education",
|
||||
"icon": "IconSchool",
|
||||
"description": "Encyclopedias, textbooks, tutorials, and educational videos for self-directed learning.",
|
||||
"language": "en",
|
||||
"tiers": [
|
||||
{
|
||||
"name": "Essential",
|
||||
"slug": "education-essential",
|
||||
"description": "Core reference materials - Wikipedia's best articles and open textbooks. Lightweight, text-focused.",
|
||||
"recommended": true,
|
||||
"resources": [
|
||||
{
|
||||
"title": "Wikipedia Top 45,000 Articles",
|
||||
"description": "The 45,000 best Wikipedia articles, optimized for size (no images)",
|
||||
"url": "https://download.kiwix.org/zim/wikipedia/wikipedia_en_top_nopic_2025-12.zim",
|
||||
"size_mb": 1880
|
||||
},
|
||||
{
|
||||
"title": "Wikibooks",
|
||||
"description": "Open-content textbooks covering math, science, computing, and more",
|
||||
"url": "https://download.kiwix.org/zim/wikibooks/wikibooks_en_all_nopic_2025-10.zim",
|
||||
"size_mb": 3100
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Standard",
|
||||
"slug": "education-standard",
|
||||
"description": "Adds educational videos, university-level tutorials, and STEM textbooks. Includes Essential.",
|
||||
"includesTier": "education-essential",
|
||||
"resources": [
|
||||
{
|
||||
"title": "TED-Ed",
|
||||
"description": "Educational video lessons on science, history, literature, and more",
|
||||
"url": "https://download.kiwix.org/zim/ted/ted_mul_ted-ed_2025-07.zim",
|
||||
"size_mb": 5610
|
||||
},
|
||||
{
|
||||
"title": "Wikiversity",
|
||||
"description": "Tutorials, courses, and learning materials for all levels",
|
||||
"url": "https://download.kiwix.org/zim/wikiversity/wikiversity_en_all_maxi_2025-11.zim",
|
||||
"size_mb": 2370
|
||||
},
|
||||
{
|
||||
"title": "LibreTexts Mathematics",
|
||||
"description": "Open-source math textbooks from algebra to calculus",
|
||||
"url": "https://download.kiwix.org/zim/libretexts/libretexts.org_en_math_2025-01.zim",
|
||||
"size_mb": 831
|
||||
},
|
||||
{
|
||||
"title": "LibreTexts Physics",
|
||||
"description": "Physics courses and textbooks",
|
||||
"url": "https://download.kiwix.org/zim/libretexts/libretexts.org_en_phys_2025-01.zim",
|
||||
"size_mb": 560
|
||||
},
|
||||
{
|
||||
"title": "LibreTexts Chemistry",
|
||||
"description": "Chemistry courses and textbooks",
|
||||
"url": "https://download.kiwix.org/zim/libretexts/libretexts.org_en_chem_2025-01.zim",
|
||||
"size_mb": 2180
|
||||
},
|
||||
{
|
||||
"title": "LibreTexts Biology",
|
||||
"description": "Biology courses and textbooks",
|
||||
"url": "https://download.kiwix.org/zim/libretexts/libretexts.org_en_bio_2025-01.zim",
|
||||
"size_mb": 2240
|
||||
},
|
||||
{
|
||||
"title": "Project Gutenberg: Education",
|
||||
"description": "Classic educational texts and resources",
|
||||
"url": "https://download.kiwix.org/zim/gutenberg/gutenberg_en_education_2025-12.zim",
|
||||
"size_mb": 606
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Comprehensive",
|
||||
"slug": "education-comprehensive",
|
||||
"description": "Complete educational library with full Wikipedia, enhanced textbooks, and TED talks. Includes Standard.",
|
||||
"includesTier": "education-standard",
|
||||
"resources": [
|
||||
{
|
||||
"title": "Wikipedia (Full, No Images)",
|
||||
"description": "Complete English Wikipedia - over 6 million articles",
|
||||
"url": "https://download.kiwix.org/zim/wikipedia/wikipedia_en_all_mini_2025-12.zim",
|
||||
"size_mb": 11400
|
||||
},
|
||||
{
|
||||
"title": "Wikibooks (With Images)",
|
||||
"description": "Open textbooks with full illustrations and diagrams",
|
||||
"url": "https://download.kiwix.org/zim/wikibooks/wikibooks_en_all_maxi_2025-10.zim",
|
||||
"size_mb": 5400
|
||||
},
|
||||
{
|
||||
"title": "TED Conference",
|
||||
"description": "Main TED conference talks on ideas worth spreading",
|
||||
"url": "https://download.kiwix.org/zim/ted/ted_mul_ted-conference_2025-08.zim",
|
||||
"size_mb": 16500
|
||||
},
|
||||
{
|
||||
"title": "LibreTexts Humanities",
|
||||
"description": "Literature, philosophy, history, and social sciences",
|
||||
"url": "https://download.kiwix.org/zim/libretexts/libretexts.org_en_human_2025-01.zim",
|
||||
"size_mb": 3730
|
||||
},
|
||||
{
|
||||
"title": "LibreTexts Geosciences",
|
||||
"description": "Earth science, geology, and environmental studies",
|
||||
"url": "https://download.kiwix.org/zim/libretexts/libretexts.org_en_geo_2025-01.zim",
|
||||
"size_mb": 1190
|
||||
},
|
||||
{
|
||||
"title": "LibreTexts Engineering",
|
||||
"description": "Engineering courses and technical references",
|
||||
"url": "https://download.kiwix.org/zim/libretexts/libretexts.org_en_eng_2025-01.zim",
|
||||
"size_mb": 678
|
||||
},
|
||||
{
|
||||
"title": "LibreTexts Business",
|
||||
"description": "Business, economics, and management textbooks",
|
||||
"url": "https://download.kiwix.org/zim/libretexts/libretexts.org_en_biz_2025-01.zim",
|
||||
"size_mb": 840
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user