This guide explains how to complete the integration of the 12 major improvements into the Ubuntu Autoinstall Configuration Builder.
The following files have been created and are ready to use:
- utils.js - Core utilities (debounce, throttle, storage, history, keyboard shortcuts)
- components.js - New React components (Collapsible sections, tooltips, modals)
- tour-config.js - Onboarding tour configuration
- manifest.json - PWA manifest
- service-worker.js - Service worker for offline support
- index.html - Updated with CDN links and CSS (partially complete)
The keyboard shortcuts system is fully cross-platform and automatically adapts to the user's operating system:
- Platform Detection: Automatically detects macOS, Windows, or Linux
- Modifier Key Mapping:
- macOS: Uses
β Commandkey (event.metaKey) - Windows/Linux: Uses
Ctrlkey (event.ctrlKey)
- macOS: Uses
- Universal Registration: Use
modin shortcut definitions - Smart Display: Shows platform-specific symbols in UI
| Platform | Modifier | Alt | Shift | Display Example |
|---|---|---|---|---|
| macOS | β (Command) | β₯ (Option) | β§ (Shift) | β S |
| Windows | Ctrl | Alt | Shift | Ctrl+S |
| Linux | Ctrl | Alt | Shift | Ctrl+S |
// β
Correct - Use 'mod' for cross-platform
shortcuts.register('mod+s', callback, 'Save'); // βS on Mac, Ctrl+S elsewhere
shortcuts.register('mod+shift+s', callback, 'Save As'); // ββ§S on Mac, Ctrl+Shift+S elsewhere
// β Incorrect - Don't hardcode 'ctrl'
shortcuts.register('ctrl+s', callback, 'Save'); // Only works with Ctrl, not Cmd on MacThe system automatically:
- Detects platform using
navigator.platform - Listens for both
ctrlKeyandmetaKeyevents - Formats display strings with correct symbols
- Shows platform indicator in shortcuts modal
To test keyboard shortcuts:
- macOS: Use Command (β) key + letter
- Windows: Use Ctrl key + letter
- Linux: Use Ctrl key + letter
All shortcuts work identically across platforms!
Add at the top of the App component (after useState declarations):
// Initialize managers (add after existing useState hooks)
const historyManager = useRef(null);
const versionManager = useRef(null);
const keyboardShortcuts = useRef(null);
const [previousYaml, setPreviousYaml] = useState('');
const [showVersions, setShowVersions] = useState(false);
const [showKeyboardHelp, setShowKeyboardHelp] = useState(false);
const [historyInfo, setHistoryInfo] = useState({ canUndo: false, canRedo: false });
// Initialize on mount
useEffect(() => {
historyManager.current = new window.utils.HistoryManager(50);
versionManager.current = new window.utils.VersionManager(50);
keyboardShortcuts.current = new window.utils.KeyboardShortcuts();
// Push initial state
historyManager.current.push(config);
// Register keyboard shortcuts
registerKeyboardShortcuts();
// Start listening
keyboardShortcuts.current.listen();
// Check if first visit and show tour
const hasSeenTour = window.utils.storage.get('hasSeenTour', false);
if (!hasSeenTour) {
setTimeout(() => startTour(), 1000);
}
// Register service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(reg => console.log('Service Worker registered:', reg))
.catch(err => console.log('Service Worker registration failed:', err));
}
return () => {
keyboardShortcuts.current.unlisten();
};
}, []);Important: Use mod instead of ctrl for cross-platform support. This automatically becomes:
β Commandon macOSCtrlon Windows/Linux
const registerKeyboardShortcuts = () => {
const shortcuts = keyboardShortcuts.current;
// Download YAML (Cmd+S on Mac, Ctrl+S on Windows/Linux)
shortcuts.register('mod+s', (e) => {
e.preventDefault();
downloadYAML();
}, 'Download YAML file');
// Undo (Cmd+Z on Mac, Ctrl+Z on Windows/Linux)
shortcuts.register('mod+z', () => {
handleUndo();
}, 'Undo last change');
// Redo (Cmd+Y on Mac, Ctrl+Y on Windows/Linux)
shortcuts.register('mod+y', () => {
handleRedo();
}, 'Redo last change');
shortcuts.register('mod+shift+y', () => {
handleRedo();
}, 'Redo (alternative)');
// Open versions
shortcuts.register('mod+k', () => {
setShowVersions(true);
}, 'Open version manager');
// Bookmark
shortcuts.register('mod+b', () => {
saveToURL();
}, 'Save to bookmark');
// Show shortcuts
shortcuts.register('mod+/', () => {
setShowKeyboardHelp(true);
}, 'Show keyboard shortcuts');
// Copy YAML
shortcuts.register('mod+shift+c', () => {
copyToClipboard();
}, 'Copy YAML to clipboard');
// Validate
shortcuts.register('mod+shift+v', () => {
handleValidate();
}, 'Validate configuration');
// Templates
shortcuts.register('mod+shift+t', () => {
setShowTemplates(true);
}, 'Open templates');
};Platform Detection: The KeyboardShortcuts class automatically detects the platform and:
- Registers shortcuts to work with both Ctrl and Cmd keys
- Displays shortcuts correctly in the UI (β on Mac, Ctrl elsewhere)
- Shows platform indicator in the shortcuts modal (π macOS, πͺ Windows, π§ Linux)
const handleUndo = () => {
const previousState = historyManager.current.undo();
if (previousState) {
setConfig(previousState);
announce('Undo successful');
updateHistoryInfo();
}
};
const handleRedo = () => {
const nextState = historyManager.current.redo();
if (nextState) {
setConfig(nextState);
announce('Redo successful');
updateHistoryInfo();
}
};
const updateHistoryInfo = () => {
setHistoryInfo({
canUndo: historyManager.current.canUndo(),
canRedo: historyManager.current.canRedo(),
...historyManager.current.getInfo()
});
};Update the existing updateConfig function:
const updateConfig = useCallback((key, value) => {
setConfig(prev => {
const newConfig = { ...prev, [key]: value };
// Add to history (debounced to avoid too many entries)
if (!window._historyDebounce) {
window._historyDebounce = window.utils.debounce((cfg) => {
historyManager.current.push(cfg);
updateHistoryInfo();
}, 500);
}
window._historyDebounce(newConfig);
return newConfig;
});
}, []);Add debounced version saving:
// Auto-save version when significant changes occur
useEffect(() => {
const debouncedSave = window.utils.debounce(() => {
if (config.hostname || config.username || config.packages) {
versionManager.current.save(config, null); // Auto-named
}
}, 5000); // Save after 5 seconds of inactivity
debouncedSave();
}, [config]);const startTour = () => {
if (window.introJs) {
const intro = window.introJs();
intro.setOptions(window.tourConfig.options);
intro.start();
intro.oncomplete(() => {
window.utils.storage.set('hasSeenTour', true);
});
intro.onexit(() => {
window.utils.storage.set('hasSeenTour', true);
});
}
};Update the YAML preview section to include diff highlighting:
// In the YAML preview section, replace the simple textarea with:
const [yamlDiff, setYamlDiff] = useState([]);
// Calculate diff when YAML changes
useEffect(() => {
if (previousYaml && previousYaml !== yaml) {
const diff = window.utils.DiffCalculator.calculateLineDiff(previousYaml, yaml);
setYamlDiff(diff);
}
setPreviousYaml(yaml);
}, [yaml]);
// Render YAML with highlighting
<div className="bg-gray-900 dark:bg-black text-gray-100 p-5 rounded-lg font-mono text-sm leading-relaxed whitespace-pre-wrap break-words max-h-[500px] overflow-y-auto">
{yamlDiff.length > 0 ? (
yamlDiff.map((line, idx) => (
<div
key={idx}
className={`${
line.type === 'added' ? 'yaml-line-added' :
line.type === 'removed' ? 'yaml-line-removed' :
line.type === 'modified' ? 'yaml-line-modified' :
''
} px-2 py-0.5`}
>
{line.line}
</div>
))
) : (
yaml
)}
</div>In the toolbar section, add undo/redo and version buttons:
{/* Add before existing toolbar buttons */}
<window.components.UndoRedoToolbar
canUndo={historyInfo.canUndo}
canRedo={historyInfo.canRedo}
onUndo={handleUndo}
onRedo={handleRedo}
historyInfo={historyInfo}
/>
<button
onClick={() => setShowVersions(true)}
className="px-3 py-2 bg-purple-600 text-white rounded-lg font-semibold hover:bg-purple-700 transition-colors text-sm"
title="Version Manager (Ctrl+K)"
>
π Versions
</button>
<button
onClick={() => setShowKeyboardHelp(true)}
className="px-3 py-2 bg-gray-600 text-white rounded-lg font-semibold hover:bg-gray-700 transition-colors text-sm"
title="Keyboard Shortcuts (Ctrl+/)"
>
β¨οΈ Shortcuts
</button>
<button
onClick={startTour}
className="px-3 py-2 bg-indigo-600 text-white rounded-lg font-semibold hover:bg-indigo-700 transition-colors text-sm"
title="Show Tour"
>
π Tour
</button>Add before the closing of the App component return statement:
{/* Version Manager Modal */}
{window.components && (
<window.components.VersionManagerModal
isOpen={showVersions}
onClose={() => setShowVersions(false)}
versionManager={versionManager.current}
onRestore={(restoredConfig) => {
setConfig(restoredConfig);
announce('Version restored');
}}
/>
)}
{/* Keyboard Shortcuts Modal */}
{window.components && (
<window.components.KeyboardShortcutsModal
isOpen={showKeyboardHelp}
onClose={() => setShowKeyboardHelp(false)}
shortcuts={keyboardShortcuts.current?.getAll()}
/>
)}Update tab components to use CollapsibleSection. Example for BasicTab:
const BasicTab = ({ config, updateConfig, darkMode }) => (
<div role="tabpanel" id="basic-panel" aria-labelledby="basic-tab" className="fade-in">
<h2 className="text-2xl font-semibold text-ubuntu-orange mb-6 pb-3 border-b-2 border-gray-200 dark:border-gray-700">
Basic Configuration
</h2>
<window.components.CollapsibleSection
title="System Settings"
icon="π₯οΈ"
defaultOpen={true}
helpText="Core system configuration"
>
<FormInput
label="Version"
id="version"
type="number"
value={config.version}
onChange={(val) => updateConfig('version', parseInt(val) || 1)}
required
helpText="Autoinstall schema version (must be 1)"
/>
<div className="flex items-center gap-2">
<FormInput
label="Locale"
id="locale"
value={config.locale}
onChange={(val) => updateConfig('locale', val)}
placeholder="en_US.UTF-8"
helpText="System language and region"
/>
<window.components.HelpTooltip
content="The locale determines the system language, date formats, and other regional settings."
learnMoreUrl="https://help.ubuntu.com/community/Locale"
/>
<window.components.InlineExample
text="Use en_US.UTF-8"
onClick={() => updateConfig('locale', 'en_US.UTF-8')}
/>
</div>
{/* Repeat for other fields */}
</window.components.CollapsibleSection>
<window.components.CollapsibleSection
title="Regional Settings"
icon="π"
defaultOpen={false}
helpText="Timezone and keyboard configuration"
>
{/* Timezone and keyboard fields */}
</window.components.CollapsibleSection>
</div>
);Add inline examples to key fields:
// Hostname examples
<window.components.InlineExample
text="web-server-01"
onClick={() => updateConfig('hostname', 'web-server-01')}
/>
// Network examples
<window.components.InlineExample
text="Basic DHCP"
onClick={() => updateConfig('networkConfig', 'version: 2\nethernets:\n eth0:\n dhcp4: true')}
/>Add these classes to elements for tour integration:
<!-- Language selector -->
<select className="language-selector ...">
<!-- Dark mode button -->
<button className="dark-mode-toggle ...">
<!-- Toolbar -->
<div className="toolbar ...">
<!-- Tabs navigation -->
<nav className="tabs-navigation ...">
<!-- YAML preview -->
<div className="yaml-preview ...">
<!-- Action buttons -->
<div className="action-buttons ...">After complete integration, users will have:
- Undo/Redo - Full history with Ctrl+Z/Ctrl+Y
- Version Management - Save, restore, export/import versions
- Keyboard Shortcuts - 15+ shortcuts for common actions
- Mobile Responsive - Touch-friendly, adaptive layouts
- Collapsible Sections - Organized, expandable form sections
- Inline Examples - Click-to-use example values
- Contextual Help - Tooltip help on every field
- YAML Diff Highlighting - See changes in real-time
- Interactive Tour - First-time user onboarding
- PWA Support - Installable, offline-capable
- Performance - Debounced saves, optimized rendering
- Type Safety - JSDoc types throughout (TypeScript-like)
After integration:
- Test undo/redo with Ctrl+Z/Ctrl+Y
- Make changes and check version auto-save (Ctrl+K to view)
- Test all keyboard shortcuts (Ctrl+/ to see list)
- Test on mobile devices (responsive layouts)
- Test tour (delete localStorage 'hasSeenTour' to reset)
- Test PWA install (Chrome: Add to Home Screen)
- Test offline mode (disable network, reload)
- Test YAML diff (make changes, see highlighting)
- History and versions use localStorage (max 50 each)
- Service worker caches all JS/CSS files
- Tour runs once per user (stored in localStorage)
- All keyboard shortcuts work globally
- Mobile improvements use CSS breakpoints at 768px and 640px
- YAML diff updates in real-time as you type
- Add TypeScript version with build process
- Add backend API for validation testing
- Add configuration marketplace/sharing
- Add more language translations
- Add advanced storage layouts (RAID, ZFS)