Skip to content

Conversation

@hypnosis
Copy link

Problem

The previous feat/space-folder-hiding approach had architectural issues:

  • Modified Obsidian system files (.obsidian/app.json, .obsidian/appearance.json)
  • Created CSS snippet files in .obsidian/snippets/
  • No cleanup on plugin unload → orphaned configurations
  • Complex UX with dry-run modal
  • Potential conflicts with other plugins

Solution

Complete refactor to Dynamic CSS injection:

Architecture

SpaceFolderHidingManager (coordinator)
    ↓
DynamicCSSManager (injects <style> into document.head)
    ↓
TrackingStore (persists state in hiding-state.json)

Key Changes

New files:

  • src/core/hiding/DynamicCSSManager.ts - CSS injection/removal
  • src/core/hiding/TrackingStore.ts - Isolated state storage
  • src/core/hiding/SpaceFolderHidingManager.ts - Main coordinator
  • src/core/hiding/index.ts - Module exports

Modified:

  • src/main.ts - Initialize hidingManager, auto-reapply on pattern change
  • src/adapters/obsidian/settings.ts - Simplified UI (toggle instead of modal)
  • src/core/react/components/System/SettingsSections/AdvancedSettings.tsx - New toggle UI

Removed:

  • Old methods: buildSpaceFolderSnippet(), syncObsidianIgnoreFilters(), etc.
  • autoApplySpaceFolderHiding setting
  • Dry-run modal complexity

How It Works

Enable:

  1. Injects <style> element into document.head
  2. Saves state to .obsidian/plugins/make-md/hiding-state.json
  3. Reindexes if pattern changed

Disable:

  1. Removes <style> from DOM
  2. Updates state to disabled
  3. Folders become visible immediately

Plugin lifecycle:

  • onload() - Restores hiding if enabled
  • onunload() - Removes CSS (folders visible)

What Is Hidden

When enabled, space folders are hidden from:

  • ✅ File Explorer
  • ✅ Search results
  • ✅ Quick Switcher
  • ✅ Link lists and backlinks

NOT hidden:

  • ❌ Graph View (requires manual configuration)

Graph View Manual Setup

Graph View uses canvas rendering and cannot be hidden via CSS. Users can manually configure:

  1. Open Graph View
  2. Settings → Filters
  3. Add pattern: _space (or custom folder name)

This is intentional - Graph View patching would require runtime modification of Obsidian internals and may break with updates.

Benefits

Technical:

  • ✅ No modification of Obsidian system files
  • ✅ Automatic cleanup on plugin unload
  • ✅ No orphaned configurations
  • ✅ Isolated state storage
  • ✅ Simpler codebase (-200 lines)

UX:

  • ✅ Simple toggle (no modal)
  • ✅ Immediate feedback
  • ✅ Clear status indicator
  • ✅ Auto-reapply on pattern change

Reliability:

  • ✅ No race conditions
  • ✅ No conflicts with other plugins
  • ✅ Graceful degradation
  • ✅ Reversible changes

Additional Fix: SystemName Persistence Bug

Problem: When switching between vaults, the System Name (vault display name) from one vault would persist and appear in other vaults.

Root cause: systemName was saved per-vault but not automatically synced with vault name on load.

Solution:

  • Added systemNameCustomized: boolean flag to settings
  • When false: systemName automatically syncs with current vault name on load
  • When user manually changes name: flag set to true to preserve custom name
  • MainMenu component now updates reactively on settings changes

Files changed:

  • src/shared/types/settings.ts - Added systemNameCustomized field
  • src/core/schemas/settings.ts - Default value false
  • src/main.ts - Auto-sync logic in loadSettings()
  • src/core/react/components/System/SettingsSections/GeneralSettings.tsx - Set flag on manual change
  • src/core/react/components/Navigator/MainMenu.tsx - Reactive UI update

Breaking Changes

Users of old feat/space-folder-hiding will have orphaned configs in:

  • .obsidian/app.json (userIgnoreFilters)
  • .obsidian/snippets/makemd-hide-space-folders.css
  • .obsidian/appearance.json (enabledCssSnippets)

These are NOT automatically removed (no migration wizard in this PR). Users can manually clean up if desired, or leave them (no harm).

Testing

Tested on:

  • ✅ Desktop (macOS)
  • ✅ Mobile (Android)
  • ✅ Enable/disable cycles
  • ✅ Pattern changes
  • ✅ Plugin reload
  • ✅ Obsidian restart
  • ✅ Vault switching with systemName sync
  • ✅ Custom systemName preservation

Comparison

Aspect Old (app.json) New (Dynamic CSS)
System files Modified Not touched
Cleanup Manual Automatic
UX Complex modal Simple toggle
State Scattered Centralized
Conflicts Possible None
Orphaned config Yes No

Future Improvements (Not in This PR)

  • Graph View optional patching (experimental flag)
  • Multiple pattern support
  • Migration wizard for old users
  • Backup/recovery UI

Recommendation: This provides a cleaner, safer, and simpler approach. Old feat/space-folder-hiding branch can be archived after merge.

Danila Susak and others added 20 commits January 14, 2026 20:48
Fixes issue where editing options in table properties would not save correctly.

Changes:
- Fix PropertyValue.tsx: Save options and colorScheme atomically to prevent data loss
- Fix EditOptionsModal.tsx: Auto-close color picker menu after color selection
- Fix OptionCell.tsx: Prioritize individual option colors over color scheme
- Fix package.json: Correct typo in dev script (nnode -> node)
- Update tsconfig.json: Change target from es6 to es2020 to support regex dotall flag
- Update .gitignore: Add data.json to ignore user settings

The main issue was that calling saveParsedValue twice would overwrite the first save
with stale data. Now both fields are saved in a single atomic operation.
Fixed issue where checkbox properties in the top section (inline properties)
were not updating when changed in the bottom Properties section.

Root cause: PropertiesView component was only listening to 'contextStateUpdated'
events, which are not triggered when individual file properties are saved via
saveProperties(). When a checkbox is toggled, it saves via saveProperties()
which triggers 'pathStateUpdated' event instead.

Solution: Added listener for 'pathStateUpdated' event to refresh property
values when the current path's properties change. This ensures both property
sections stay in sync.

Changes:
- Added pathChanged() handler to listen for pathStateUpdated events
- Added pathState to useEffect dependencies for proper cleanup
- Properties now refresh immediately when any property is saved
**Bug Description:**
When adding or modifying select (option) property values in the Properties
panel, the values were being incorrectly serialized as JSON arrays
(e.g., ["approve"]) instead of plain strings (e.g., "approve"). This caused
the values to display with quotes in the UI and broke the select dropdown
functionality.

**Root Causes:**
1. OptionCell was using serializeMultiDisplayString() for single select values,
   which was unnecessary and caused incorrect serialization
2. parseProperty() was called without explicit type information, causing
   detectPropertyType() to misidentify single option arrays as option-multi
3. No safeguards existed to handle array values when parsing single options

**Changes Made:**

1. **OptionCell.tsx**: Changed single option value serialization from
   serializeMultiDisplayString() to direct value access (value[0] ?? "")
   in savePropValue() and removeOption() methods

2. **PropertiesView.tsx**: Modified property parsing to pass explicit column
   type to parseProperty(), preventing type misdetection

3. **parsers.ts**: Added array handling in parseProperty() for option type
   to extract first element if value is unexpectedly an array

4. **properties.ts**: Added safety check in parseMDBStringValue() to parse
   and extract single values from JSON array strings when saving to frontmatter

**Testing:**
- Single select options now save and display correctly without quotes
- Multi-select options continue to work as expected
- Select dropdowns function properly after value changes
…e misdetection

**Bug Description:**
When property values were parsed from frontmatter in PropertiesView and
syncContextRow, the type parameter was not passed to parseProperty(). This
caused detectPropertyType() to auto-detect types based on the value format
rather than using the actual field schema definition.

For example, a single-select option field with an array value like ["approve"]
in frontmatter would be misdetected as option-multi instead of option, causing:
1. Values to display incorrectly with quotes
2. Checkbox properties not syncing between top and bottom views

**Root Cause:**
Two locations were calling parseProperty() without the type parameter:
1. PropertiesView.tsx line 100 - when loading properties from frontmatter
2. linkContextRow.ts line 94 - when syncing context rows with frontmatter

This caused the system to rely on detectPropertyType() which makes assumptions
based on value format (arrays → multi-type) rather than using the actual
schema definition.

**Changes Made:**

1. **PropertiesView.tsx** (line 87-95):
   - Added lookup in both tableData.cols AND columns (from context schemas)
   - This ensures properties defined in context schemas are found correctly
   - Pass the resolved field type to parseProperty() on line 100

2. **linkContextRow.ts** (line 92-96):
   - Modified filteredFrontmatter reduce function to find field type from
     fields array before calling parseProperty()
   - Pass the field type as third parameter to parseProperty()

**Impact:**
- Single-select options now parse correctly even when stored as arrays
- Boolean checkboxes sync properly between top properties and bottom panel
- Multi-select options continue to work as expected
- Type detection now respects schema definitions over value format

**Testing:**
- ✅ Single select with array value displays without quotes
- ✅ Boolean checkboxes sync between top and bottom views
- ✅ Multi-select options work correctly
- ✅ Properties from context schemas resolve proper types
…ntmatter

Fix: Select option values displaying with quotes and checkbox sync issues
…+ dry-run apply/undo

Problem:
- Make.md stores space metadata/config under a dedicated folder (default: .space).
- Obsidian Sync has a known limitation: hidden (dot-prefixed) folders may not sync reliably.
- Users rename the folder to a non-hidden name (e.g. _space, __space__, new_space) so Sync can include it.
- But once the folder is not hidden, it becomes visible in File Explorer, appears in Graph, and is considered during indexing/search unless users manually configure exclusions in multiple places.

Goal:
When the Make.md space config folder name is changed to a non-hidden folder, provide a way to automatically exclude it from:
- File tree visibility (UI)
- Graph visibility
- Indexing/search visibility
…without requiring manual Obsidian global configuration each time.

Solution overview:
This PR adds two ways to apply "space folder hiding" after renaming the folder:

1) Automatic mode (OFF by default)
- New setting: "Auto-apply space folder hiding" (autoApplySpaceFolderHiding)
- When enabled, changing "Space Folder Name" automatically:
  - Updates .obsidian/app.json → userIgnoreFilters with pattern **/<spaceFolderName>/**
  - Writes/enables .obsidian/snippets/makemd-hide-space-folders.css via .obsidian/appearance.json
  - Reindexes Make.md paths/spaces

2) Manual mode (recommended default) via Dry-run popup
- New button: "Reapply space folder hiding" (placed right after "Space Folder Name")
- Opens a popup that shows a human-readable Dry-run of changes:
  - what will be added/removed in userIgnoreFilters
  - which CSS snippet will be written/enabled
  - that Make.md will reindex
- Buttons:
  - Apply: performs changes + reindex
  - Undo: removes the ignore filter, disables & deletes the snippet, then reindexes

Implementation details:
- Extend Make.md internal path filtering so the configured space folder name is treated as hidden even when it is not dot-prefixed:
  - hide the folder itself and all nested files (matches any path segment equal to spaceSubFolder)
- Fix Space node visibility in Make.md:
  - previously, hidden for type: 'space' nodes was effectively hardcoded to false
  - now hidden is derived from the same exclusion predicate used for paths
- Add UI both in:
  - Obsidian Settings → Make.md → Advanced
  - Make.md internal Settings UI → Advanced

Behavior before:
- .space could be hidden, but Obsidian Sync may not sync it reliably.
- Renaming to a non-hidden folder allowed sync, but the folder became visible everywhere and required manual exclusions.

Behavior after:
- Users can safely rename the space config folder to a non-hidden name for Sync compatibility.
- They can then:
  - enable Auto-apply (optional), or
  - use the Manual Dry-run popup to apply/revert changes safely
…to keep the folder out of the file tree, graph, and indexing.

Constraints / limitations:
- This does not change Obsidian Sync behavior; it only provides a workflow compatible with its limitations.
- Undo reverts config/snippet changes; it does not rename/delete any user folders.
- Some Obsidian UI effects may require a reload depending on Obsidian's config reload behavior.
- Fix Spaces header overlapping with system status bar on iPhone
- Add env(safe-area-inset-top) padding for mobile workspace drawer header
- Fixes issue where Spaces bar was hidden under notch/Dynamic Island
- Change from env(safe-area-inset-top, 0) to calc(env(safe-area-inset-top, 0px) + 12px)
- Apply to both workspace-drawer-header and mk-focuses
- Ensures Spaces header is fully visible below notch/Dynamic Island
- Apply env(safe-area-inset-top) directly to workspace-drawer-inner container
- This ensures the entire Spaces panel starts below the notch/Dynamic Island
- Previous fix only affected the header, not the content area
Problem:
On mobile devices with notch/Dynamic Island, the Spaces panel with
circular icons was overlapping the system status bar area, making
the icons invisible.

Solution:
1. Focuses.css:
   - Changed padding-top from 16px to 12px for better balance
   - Set top: 0 for nav-header (instead of safe-area-inset-top)
   - Added background-color and z-index for proper sticky positioning
   - Added padding-bottom: 8px for visual balance

2. FileTree.css:
   - Added rule for .is-mobile (not only is-phone)
   - Now workspace-drawer-inner has padding-top on all mobile devices

Logic:
Parent container (workspace-drawer-inner) has safe-area padding,
and Spaces panel sticks to the top of container (top: 0), automatically
accounting for safe-area. Additional padding inside mk-focuses provides
visual comfort.

Tested on Pixel 7 Pro emulator (Android).
Replace app.json modification approach with dynamic CSS injection for
hiding space folders from Obsidian UI.

Changes:
- Add DynamicCSSManager: injects/removes CSS from DOM
- Add TrackingStore: persists state in hiding-state.json
- Add SpaceFolderHidingManager: coordinates hiding system
- Update main.ts: initialize manager, handle lifecycle
- Simplify AdvancedSettings.tsx: toggle instead of modal
- Remove dependency on app.json and CSS snippet files

Benefits:
- No modification of Obsidian system files
- Automatic cleanup on plugin unload
- Simpler UX (toggle checkbox)
- No orphaned configurations

Technical details:
- CSS injected as <style> element in document.head
- State stored in .obsidian/plugins/make-md/hiding-state.json
- Auto-reapply when space folder name changes
- Graph View requires manual configuration (documented)

Breaking change: Previous app.json modifications not auto-removed
Remove old methods from main.ts:
- buildSpaceFolderSnippet
- readJSONFromVaultConfig
- writeJSONToVaultConfig
- dryRunSpaceFolderHiding
- applySpaceFolderHiding
- undoSpaceFolderHiding
- syncObsidianIgnoreFilters
- syncSpaceFolderSnippet
- reindexAfterSpaceFolderChange

Fix imports:
- TrackingStore: shared/utils/json (not strings)
- index.ts: export type { HidingState }
Added systemNameCustomized flag to track user customization. When false, systemName automatically syncs with vault name on load. When user manually changes systemName, flag is set to true to preserve custom name. MainMenu component now updates reactively on settings changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant