Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bca2135
fix: atomic save for option properties and color scheme
Jan 14, 2026
92e1e4e
fix: sync checkbox state between properties sections
Jan 14, 2026
cfb90a8
fix: prevent select option values from being serialized as JSON arrays
Jan 15, 2026
82474c4
fix: pass explicit field types when parsing properties to prevent typ…
Jan 15, 2026
d698cde
Merge pull request #1 from hypnosis/bugfix/select-property-quotes-fro…
hypnosis Jan 15, 2026
8c3eb6c
feat: support non-hidden space config folder with optional auto-hide …
hypnosis Jan 16, 2026
1190566
fix: mobile safe area inset for iOS Dynamic Island
hypnosis Jan 16, 2026
90130b9
fix: increase safe-area padding for better visibility
hypnosis Jan 16, 2026
46c2a61
fix: add safe-area padding to workspace-drawer-inner
hypnosis Jan 16, 2026
28abb4b
fix: align mobile spaces bar with safe area
hypnosis Jan 16, 2026
daf9158
fix: normalize mobile spaces bar spacing
hypnosis Jan 16, 2026
7fd1188
Revert "fix: normalize mobile spaces bar spacing"
hypnosis Jan 16, 2026
ebe011f
fix: tweak navigator spacing under spaces bar
hypnosis Jan 16, 2026
4c7591b
chore: bump version to 1.3.3-beta.4
hypnosis Jan 16, 2026
c53de84
fix: refine mobile spaces navigator spacing
hypnosis Jan 16, 2026
7d51fad
fix: Spaces panel overlapping safe area on mobile devices
hypnosis Jan 17, 2026
6932e7c
chore: revert version to 1.3.3
hypnosis Jan 17, 2026
a2089ff
refactor: implement dynamic CSS-based space folder hiding
hypnosis Jan 17, 2026
f1462ce
fix: remove old space folder hiding code and fix imports
hypnosis Jan 18, 2026
206cb9a
fix: prevent systemName from persisting across vault switches
hypnosis Jan 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ node_modules
.env
.DS_Store
undefined
.vscode
.vscode
data.json
280 changes: 145 additions & 135 deletions main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
"author": "make.md",
"authorUrl": "https://www.make.md",
"isDesktopOnly": false
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "make.md",
"main": "main.js",
"scripts": {
"dev": "nnode esbuild.config.mjs",
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"preview": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs preview",
"demo": "&& node esbuild.config.mjs demo",
Expand Down
32 changes: 32 additions & 0 deletions src/adapters/obsidian/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,40 @@ export class MakeMDPluginSettingsTab extends PluginSettingTab {
settings.settings.forEach((setting) => {
if (setting.category === category && !setting.subCategory) {
insertSetting(containerEl, setting);
if (category === "advanced" && setting.name === "spaceSubFolder") {
// Hide space folders toggle
const hidingManager = this.plugin.hidingManager;
const isEnabled = hidingManager?.isEnabled() ?? false;
const currentPattern = hidingManager?.getCurrentPattern();

const hidingSetting = new Setting(containerEl)
.setName("Hide space folders from UI")
.setDesc(
"Hides space folders from File Explorer, Search, and Quick Switcher. " +
"Uses dynamic CSS (works only when plugin is enabled). " +
"Status: " + (isEnabled && currentPattern ? `Active (hiding: ${currentPattern})` : "Disabled") + ". " +
"Note: Graph View requires manual configuration."
);

hidingSetting.addToggle((toggle) =>
toggle.setValue(isEnabled).onChange(async (value: boolean) => {
try {
if (value) {
await hidingManager?.enable(this.plugin.superstate.settings.spaceSubFolder);
} else {
await hidingManager?.disable();
}
// Refresh settings display
this.display();
} catch (err) {
console.error("Failed to toggle hiding:", err);
}
})
);
}
}
});

settings.subCategories[category].forEach((subCategory) => {
const subCategoryItems = settings.settings.filter((setting) => setting.category === category && setting.subCategory === subCategory);
if (subCategoryItems.length > 0) {
Expand Down
119 changes: 119 additions & 0 deletions src/core/hiding/DynamicCSSManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* DynamicCSSManager
*
* Manages dynamic CSS injection for hiding space folders from Obsidian UI.
* Uses DOM <style> element instead of CSS snippet files for automatic cleanup.
*/

export class DynamicCSSManager {
private styleElement: HTMLStyleElement | null = null;
private currentPattern: string | null = null;

/**
* Apply CSS hiding for the given pattern
* @param pattern - Folder name to hide (e.g., "_space", ".space")
*/
public apply(pattern: string): void {
// Remove existing style if any
this.remove();

// Create new style element
this.styleElement = document.createElement('style');
this.styleElement.id = 'makemd-dynamic-space-hiding';
this.styleElement.setAttribute('data-makemd-managed', 'true');
this.styleElement.textContent = this.buildCSS(pattern);

// Inject into DOM
document.head.appendChild(this.styleElement);

this.currentPattern = pattern;
}

/**
* Remove CSS hiding
*/
public remove(): void {
if (this.styleElement) {
this.styleElement.remove();
this.styleElement = null;
}
this.currentPattern = null;
}

/**
* Check if hiding is currently active
*/
public isActive(): boolean {
return this.styleElement !== null && document.head.contains(this.styleElement);
}

/**
* Get current pattern
*/
public getCurrentPattern(): string | null {
return this.currentPattern;
}

/**
* Rebuild CSS with new pattern
* @param pattern - New folder name to hide
*/
public rebuild(pattern: string): void {
this.apply(pattern);
}

/**
* Build CSS rules for hiding the pattern
* @param pattern - Folder name to hide
*/
private buildCSS(pattern: string): string {
const escapedPattern = this.escapeForCSS(pattern);

return [
'/* Auto-generated by Make.md - Dynamic Space Folder Hiding */',
'/* DO NOT EDIT - This style is managed automatically */',
'',
'/* File Explorer */',
`.nav-folder-title[data-path*="${escapedPattern}"],`,
`.tree-item[data-path*="${escapedPattern}"],`,
`.tree-item-self[data-path*="${escapedPattern}"] {`,
' display: none !important;',
'}',
'',
'/* Search Results */',
`.search-result-file-title[data-path*="${escapedPattern}"],`,
`.search-result-container[data-path*="${escapedPattern}"] {`,
' display: none !important;',
'}',
'',
'/* Quick Switcher */',
`.suggestion-item[data-path*="${escapedPattern}"],`,
`.suggestion-content[data-path*="${escapedPattern}"] {`,
' display: none !important;',
'}',
'',
'/* Link lists and backlinks */',
`.tree-item-inner[data-path*="${escapedPattern}"] {`,
' display: none !important;',
'}',
'',
'/* Files inside space folder */',
`.nav-file-title[data-path*="${escapedPattern}/"] {`,
' display: none !important;',
'}',
'',
'/* Note: Graph View requires manual configuration */',
'/* See documentation for Graph View setup */',
].join('\n');
}

/**
* Escape special characters for CSS attribute selector
* @param pattern - Pattern to escape
*/
private escapeForCSS(pattern: string): string {
// Escape special CSS characters
// For attribute selectors, we need to escape: " \
return pattern.replace(/["\\]/g, '\\$&');
}
}
118 changes: 118 additions & 0 deletions src/core/hiding/SpaceFolderHidingManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* SpaceFolderHidingManager
*
* Main manager for space folder hiding functionality.
* Coordinates DynamicCSSManager and TrackingStore.
*/

import { DataAdapter } from 'obsidian';
import { DynamicCSSManager } from './DynamicCSSManager';
import { TrackingStore, HidingState } from './TrackingStore';

export class SpaceFolderHidingManager {
private cssManager: DynamicCSSManager;
private tracking: TrackingStore;
private reindexCallback: () => Promise<void>;

constructor(
adapter: DataAdapter,
configDir: string,
reindexCallback: () => Promise<void>
) {
this.cssManager = new DynamicCSSManager();
this.tracking = new TrackingStore(adapter, configDir);
this.reindexCallback = reindexCallback;
}

/**
* Initialize - load state and restore if needed
*/
public async initialize(): Promise<void> {
const state = await this.tracking.load();

// Restore hiding if it was enabled
if (state.mode === 'dynamic' && state.pattern) {
this.cssManager.apply(state.pattern);
}
}

/**
* Enable hiding for the given pattern
*/
public async enable(pattern: string): Promise<void> {
if (!pattern || pattern.trim().length === 0) {
throw new Error('Pattern cannot be empty');
}

const previousPattern = this.tracking.getPattern();
const needsReindex = previousPattern !== pattern;

// Apply CSS
this.cssManager.apply(pattern);

// Save state
await this.tracking.save({
mode: 'dynamic',
pattern: pattern,
applied_at: Date.now(),
css_injected: true,
});

// Reindex if pattern changed
if (needsReindex) {
await this.reindexCallback();
}
}

/**
* Disable hiding
*/
public async disable(): Promise<void> {
// Remove CSS
this.cssManager.remove();

// Save state
await this.tracking.save({
mode: 'disabled',
pattern: null,
applied_at: null,
css_injected: false,
});
}

/**
* Check if hiding is enabled
*/
public isEnabled(): boolean {
return this.cssManager.isActive();
}

/**
* Get current pattern
*/
public getCurrentPattern(): string | null {
return this.cssManager.getCurrentPattern();
}

/**
* Get current state
*/
public getState(): HidingState {
return this.tracking.getState();
}

/**
* Check if reapply is needed (pattern mismatch)
*/
public needsReapply(currentPattern: string): boolean {
const activePattern = this.getCurrentPattern();
return this.isEnabled() && activePattern !== currentPattern;
}

/**
* Cleanup - called on plugin unload
*/
public cleanup(): void {
this.cssManager.remove();
}
}
Loading