Lapel Obsidian Plugin

repository·main·Indexed 40 days ago

https://github.com/liamcain/obsidian-lapel

An Obsidian plugin that adds visual heading level indicators (markers) to the editor gutter to help users navigate document structure. It includes a menu for quick switching of heading levels and supports CSS customization of markers via the .cm-heading-marker class and --heading-marker variable. Version 0.1.10.

Tokens
2K
Snippets
9
Records
12
Agent score
50%

What's inside Lapel

  1. Overview of Lapel

    main
    Lapel is an Obsidian plugin designed to help users visually distinguish between heading levels. It works by displaying a marker (e.g., h1, h2, h3) next to each heading in the editor gutter. It also provides a menu to allow for quick switching of heading levels.
  2. Install dependencies for Lapel development

    main

    To set up the development environment, ensure you have Node.js (LTS recommended) and pnpm installed. Use pnpm exclusively to ensure dependency versions match the pnpm-lock.yaml file. Do not use npm or yarn.

    pnpm install
  3. Manual smoke test checklist for Lapel

    main

    Because Lapel does not have an automated test suite, developers must perform manual smoke testing in a real Obsidian vault to verify stability. Ensure the following behaviors are correct:

    1. Linting: pnpm run lint must exit with code 0.
    2. Production Build: pnpm run build must complete without errors or warnings.
    3. Gutter Markers: Markers must appear next to each heading in the editor.
    4. Heading Menu: Clicking a marker must open the heading-level menu; selecting a level must rewrite the heading correctly.
    5. Settings Integration: Toggling "show before/after line numbers" must move the gutter as expected.
    6. Table Cells: Markers must disappear inside table cell editors.
    7. Folding: Folded headings must change level cleanly without wiping the block content.
    8. RTL Support: Markers must align correctly in Right-to-Left mode.
    9. Popout Windows: The heading menu must attach to the correct window when using popouts.
  4. Develop Lapel against a local Obsidian vault

    main

    Since esbuild does not automatically copy files into your Obsidian vault, use one of the following two workflows to test your changes:

    Create a symbolic link from your repository into your test vault's plugin folder. This allows pnpm run dev to update the main.js file directly inside the vault.

    ln -s "$PWD" "/path/to/TestVault/.obsidian/plugins/lapel"

    After rebuilding, reload the plugin in Obsidian by toggling it off and on in Settings → Community plugins, or use the Hot Reload community plugin.

    Option 2: Build and Copy

    Manually build the production files and copy them to the vault's plugin folder.

    pnpm run build
    cp main.js manifest.json styles.css /path/to/TestVault/.obsidian/plugins/lapel/
    # Symlink approach
    ln -s "$PWD" "/path/to/TestVault/.obsidian/plugins/lapel"
    pnpm run dev
    
    # Build and copy approach
    pnpm run build
    cp main.js manifest.json styles.css /path/to/TestVault/.obsidian/plugins/lapel/
  5. Configure Lapel settings

    main

    Lapel provides the following configuration options in the Obsidian settings menu:

    • Show before line numbers: When enabled, heading levels are displayed before the line numbers in the editor gutter. By default, they are displayed after the line numbers.
    • Show in source mode: Enables or disables the visibility of heading levels when Obsidian is in Source Mode.
  6. Customize heading markers with CSS

    main

    You can target specific heading levels for visual customization using CSS. Each heading marker uses the .cm-heading-marker class and a data-level attribute corresponding to the heading level (e.g., "1" for H1, "2" for H2). You can change the appearance by overriding the --heading-marker CSS variable.

    Example: To change the H6 marker to a burger menu icon:

    .cm-heading-marker[data-level="6"] {
      --heading-marker: "☰";
    }
  7. Configure headingMarkerPlugin options

    main

    When calling headingMarkerPlugin, you can provide an options object of type HeadingMarkerPluginOpts to control visibility and gutter placement:

    OptionTypeDescription
    showBeforeLineNumbersbooleanIf true, the gutter is given high precedence (Prec.high) to appear before line numbers. If false, it uses Prec.low.
    showInSourceModebooleanIf true, heading markers will be rendered even when the editor is in Source Mode. If false, they only render in Live Preview mode.
    interface HeadingMarkerPluginOpts {
      showBeforeLineNumbers: boolean;
      showInSourceMode: boolean;
    }
  8. Build and lint Lapel

    main

    Lapel uses esbuild to bundle the plugin into a single main.js file. The following commands are available via pnpm:

    • pnpm run lint: Runs ESLint over all .ts files. This must pass before any release.
    • pnpm run dev: Runs esbuild in watch mode with inline sourcemaps. It outputs main.js to the repo root.
    • pnpm run build: Runs lint first, then performs a minified production esbuild. It outputs a production main.js (without sourcemaps) to the repo root.
    pnpm run lint
    pnpm run dev
    pnpm run build
  9. Use LapelPlugin to manage heading markers

    main

    The LapelPlugin class is the main entrypoint for the Obsidian plugin. It manages the lifecycle of heading markers within the editor using CodeMirror extensions. It provides a mechanism to update settings dynamically, which triggers a workspace update to reflect changes in how heading markers are displayed (e.g., whether they appear before line numbers or in source mode).

    // Note: As an Obsidian plugin, this is typically instantiated by the Obsidian app.
    // Developers interacting with the plugin instance can use updateSettings to change behavior.
    
    await plugin.updateSettings((oldSettings) => ({
      showBeforeLineNumbers: true,
      showInSourceMode: false
    }));
  10. Initialize the headingMarkerPlugin

    main

    The headingMarkerPlugin function creates a CodeMirror view plugin that adds heading level markers to the editor gutter. It provides a visual indicator of heading levels and allows users to change heading levels via a context menu when clicking the marker.

    It returns an array containing the marker plugin and a gutter plugin. The gutter's priority (precedence) is determined by the showBeforeLineNumbers option.

    import { headingMarkerPlugin } from './src/headingWidget';
    
    const plugin = headingMarkerplugin({
      showBeforeLineNumbers: true,
      showInSourceMode: false
    });
  11. Update Lapel settings via updateSettings()

    main

    The updateSettings method allows for transactional updates to the plugin's configuration. It accepts a transformation function tx that receives the current LapelSettings and returns a Partial<LapelSettings>.

    If the updated settings affect visual markers (specifically showBeforeLineNumbers or showInSourceMode), the plugin automatically regenerates the CodeMirror extension and calls this.app.workspace.updateOptions() to apply the changes immediately to the editor.

    /**
     * @param tx A function that takes the current settings and returns the changes to apply.
     */
    public async updateSettings(
        tx: (old: LapelSettings) => Partial<LapelSettings>
      ): Promise<void>
  12. LapelSettings Interface

    main

    The LapelSettings interface defines the schema for the plugin's configuration. It contains the following boolean properties:

    • showBeforeLineNumbers: Controls the gutter position of heading markers.
    • showInSourceMode: Controls visibility in Obsidian's source mode.
    export interface LapelSettings {
      showBeforeLineNumbers: boolean;
      showInSourceMode: boolean;
    }