A desktop Electron application for editing mulle-match pattern files in a visual, dual-column interface. The app manages files in .mulle/etc/match and .mulle/share/match directories.
.mulle/
βββ etc/match/
β βββ ignore.d/ # User overrides (higher priority)
β β βββ 10-boring--none -> ../../../share/match/ignore.d/10-boring--none
β β βββ 20-generated--none
β βββ match.d/ # User overrides (higher priority)
βββ share/match/
βββ ignore.d/ # Shared defaults
β βββ 10-boring--none
βββ match.d/ # Shared defaults
βββ 10-sourcetree--all
βββ 65-generated--clib
βββ 70-header--private-generated-headers
βββ ... (more pattern files)
- Two-tier system:
share/contains defaults,etc/contains overrides - Active files: Combination of both directories (etc takes precedence)
- Symlinks: May be used instead of copies on supporting platforms
- Two columns: Left = ignore.d patterns, Right = match.d patterns
- Create project structure
- Set up package.json with dependencies
- Create main.js with basic window
- Create empty HTML/CSS layout
- Set up menu with keyboard shortcuts
- Configure GitHub workflows
- Add README and LICENSE
- Implement directory picker dialog
- Scan
.mulle/etc/matchand.mulle/share/match - Detect symlinks vs regular files
- Build file list for both ignore.d and match.d
- Display project path in UI
- Handle missing directories gracefully
- Validate .mulle folder exists before opening
- Show error dialog if no .mulle folder found
- Create file card component for each pattern file
- Show file name and location (etc vs share)
- Indicate if file is symlink
- Display file selector/list in each column
- Sort files by name
- Highlight selected file
- Open multiple editors simultaneously
- Close editors with confirmation if modified
- Show modified indicator (β) in editor header
- File list scrollable, editors take remaining space
- Create textarea-based editor for each file
- Load file contents when selected
- Implement syntax highlighting for patterns (optional - skip for now)
- Track modified state (dirty flag)
- Auto-resize editor based on content
- Allow multiple files open simultaneously
- Add new file button with modal dialog
- File naming convention:
NN-type--categoryformat - Three-field input (priority, type, category) with live preview
- Delete file button (only shown for etc/ files)
- Duplicate filename prevention (checks both etc/ and share/)
- Auto-create symlinks to share/ files when creating first etc/ file
- Scroll to and focus new editor after creation
- Sort editors alphabetically by filename
- Handle symlink deletion (just removes the symlink)
- Implement "Save All" functionality
- Detect which files need saving (modified flag)
- Handle symlinks correctly:
- Preserve symlinks when content remains identical to share/
- Convert files to symlinks when content becomes identical to share/
- Replace symlinks with real files when content differs from share/
- Handle share/ files: Create in etc/ (as symlink if identical, real file if different)
- Auto-create parent directories if they don't exist
- Delete marked files on save
- Optimize etc/ structure after save (symlink creation/cleanup)
- Remove etc/ directory if all files are symlinks (no unique content)
- Update modified indicators after save
- Show save confirmation
- Reload project after save to reflect new state
- Recent projects list (up to 10, stored in userData)
- Preferences modal with settings
- Show/hide location badges (ETC, SHARE, SYMLINK)
- Environment variable editor (MULLE_MATCH_*)
- Live preview panel showing matched files
- Debounced preview updates (1s delay, 5s max)
- Split view (ignore.d / match.d columns)
- Multiple editors open simultaneously
- Keyboard shortcuts for all main actions
- Search/filter for files
- Collapsible/expandable editor panels
- Drag-and-drop file reordering
- Validate pattern syntax
- Test with real mulle projects
- Test symlink handling on Linux/Mac/Windows
- Test with missing directories
- Test with read-only files
- Error handling and user feedback
- Create proper icon set
- Add tooltips and help text
- Improve error messages
- Add preferences/settings
- Create user documentation
- Test builds on all platforms
- Create release builds
Pattern files follow the format: NN-type--category
- NN: Two-digit priority (00-99) for sorting order
- type: File purpose (e.g., "header", "source", "boring", "generated", "resource")
- category: Category name (e.g., "all", "none", "private-headers", "myfiles")
- Examples:
10-boring--none,50-resource--whatever,85-header--public-headers
The editor maintains a smart symlink system:
- Default state: etc/ files that are identical to share/ are stored as symlinks
- On edit: Symlinks are replaced with real files when content differs
- On save: Files that become identical to share/ are converted back to symlinks
- First etc/ file: When creating the first file in etc/match/ignore.d or etc/match/match.d, all share/ files are automatically symlinked into etc/
- Cleanup: If all etc/ files become symlinks (no unique content), the entire etc/ directory is removed
This approach:
- Saves disk space (identical files are symlinks)
- Shows relationship visually (symlink badge)
- Allows deletion of etc/ when user has no customizations
- Makes it clear which files have local overrides
For each modified file:
1. Determine target path (etc/ for new files, original path for existing)
2. Check if corresponding share/ file exists
3. Compare content with share/ version
4. If identical:
- Remove any existing file/symlink
- Create symlink to share/
5. If different:
- Remove any existing file/symlink
- Write real file with new content
6. After all saves, optimize etc/ structure:
- For each file, check if now identical to share/
- Convert to symlinks where possible
- Remove etc/ directory if only symlinks remain
- File > Open Project (Ctrl+O): Open project directory
- File > Save All (Ctrl+S): Save all modified files
- Edit > Add File (Ctrl+N): Create new pattern file
- Edit > Delete File (Ctrl+Backspace): Delete selected file
- Use Node.js
fs.promisesfor async file operations - Detect symlinks with
fs.lstat() - Read file contents with
fs.readFile() - Write files with automatic parent directory creation
- Create relative symlinks:
../../../share/match/{subdir}/{filename}
- Native HTML textarea for editing (auto-sizing by line count)
- CSS Grid for two-column layout
- Flexbox for file lists
- Modal dialogs for confirmations and input forms
- Live filename preview in add file modal
- DELETE button only shown for etc/ files (not share/)
- Modified indicator (β) in editor title
- Location badges (ETC, SHARE, SYMLINK) - toggleable in preferences
The editor manages three mulle-match environment variables:
MULLE_MATCH_FILENAMES: File patterns to matchMULLE_MATCH_IGNORE_PATH: Paths to ignoreMULLE_MATCH_PATH: Search paths
Changes are saved via mulle-env set and loaded via mulle-env get.
- User opens project directory
- App validates
.mullefolder exists - App scans
.mulle/etc/matchand.mulle/share/match(both ignore.d and match.d) - Build file registry with metadata (name, location, path, isSymlink)
- Filter files: etc/ overrides share/ for same filename
- Auto-open all unique files in editor panels
- Load environment variables from
mulle-env - Initialize live preview with
mulle-match - User edits β mark as modified, schedule preview update
- User saves β smart save logic applies:
- Save modified files (create etc/ if needed)
- Auto-create symlinks to share/ files if this is first etc/ file
- Compare with share/ versions, convert to symlinks if identical
- Clean up etc/ directories that only contain symlinks
- Reload project to show updated state (symlinks, badges, etc.)
- Critical: Always compare content with share/ before writing
- Never blindly overwrite symlinks - check content first
- When creating first etc/ file, must symlink ALL share/ files to maintain proper pattern precedence
- Users should never reuse share/ filenames (prevents shadowing)
- Electron renderer doesn't support
prompt()- must use custom modals - Use separate input fields for structured data (priority, type, category)
- Live preview helps users understand the result before committing
- Always use
{ recursive: true }when creating directories - Check if directories exist before determining if it's the "first file"
- Create parent directories automatically in both
writeFileandcreateSymlinkoperations
The mulle-match system uses this precedence (highest to lowest):
- Files in
etc/match/{ignore,match}.d/(user overrides) - Files in
share/match/{ignore,match}.d/(shared defaults)
When both exist with same name, etc/ wins. The editor must maintain this by:
- Only showing one version in UI (etc/ takes precedence)
- Creating new files always in etc/
- Preventing duplicate filenames across both locations
The editor uses color saturation to indicate file origins:
| File Origin | Header Color | Description |
|---|---|---|
| share/ files | Desaturated gray (#f5f6f7) | Viewing defaults |
| etc/ symlinks | Light blue (#e8f2fc) | Linked to defaults |
| etc/ real files | Saturated blue (#d4e4f7) | Custom overrides |
Modification indicators:
- Bold title - File has been edited
- Red pulsing dot (β) - Unsaved changes
- Header turns saturated blue - When editing starts (shows file will become etc/)
Show/Hide Deleted Files (View menu):
- Deleted files are hidden by default
- Toggle visibility to see files marked for deletion
- Deleted files show with trash icon (ποΈ) and red tint
- DELETE button becomes green UNDELETE button
- Files can be restored before saving
Revert Options (File menu):
-
Revert to Saved (Ctrl+R)
- Discards all unsaved edits and deletions
- Reloads project from disk
-
Revert to Defaults (Ctrl+Shift+R)
- Permanently deletes all etc/match/ directories
- Only enabled when both etc/ and share/ files exist
- Two confirmation dialogs (safety feature)
- Results in using only share/ defaults
Pattern files use strict naming: NN-type--category
| Component | Rule | Examples | Invalid |
|---|---|---|---|
| NN | Exactly 2 digits (00-99) | 05, 10, 50, 99 |
5, 100, ab |
| type | Identifier (a-z, 0-9, _) | custom, header_2 |
my-type, my type |
| category | Identifier (a-z, 0-9, _) | myfiles, stage_2 |
my-files, my files |
Input Validation:
- Real-time filtering: Invalid characters stripped immediately
- Auto-padding: Single digit priority (e.g.,
5) becomes05 - Visual hints: Gray help text below each field
- HTML5 patterns: Browser-level validation
- Clear error messages: Detailed explanations on validation failure
- Pattern syntax validation with highlighting
- Preview mode (test patterns against files)
- Diff view for etc vs share files
- Undo/redo support
- Auto-save functionality
- Integration with mulle-match CLI tools
- Pattern templates/snippets
- Can open any mulle project
- Can edit all pattern files
- Smart save handles symlinks correctly
- Works on Linux, macOS, and Windows
- Intuitive UI that's faster than CLI editing
- No data loss (atomic saves, backups)
- Visual feedback for file origins and modifications
- Strict filename validation prevents errors