ts-obsidian-ui-settings 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,38 @@
1
+ [BOTTOM](#100---2026-01-11) [LICENSE](LICENSE) [ROADMAP](ROADMAP.md) [README](README.md)
2
+
3
+ # Changelog
4
+
5
+ All notable changes to this project will be documented in this file.
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+
11
+ - No additions yet
12
+
13
+ ### Changed
14
+
15
+ - No changes yet
16
+
17
+ ### Fixed
18
+
19
+ - No fixes yet
20
+
21
+ ## [1.0.0] - 2026-01-11
22
+
23
+ - Initial version
24
+
25
+ ### Features
26
+
27
+ - Type-safe, extensible framework for Obsidian plugin settings
28
+ - Support for multiple sub-tabs with compile-time enforced unique IDs
29
+ - Declarative sub-tab registry for centralized management
30
+ - Decoupled `PluginSettingsRenderContext` for isolated rendering
31
+ - Automatic navigation and active-tab state handling
32
+ - Seamless integration with `ts-obsidian-plugin` settings management
33
+ - Designed for TypeScript and Rollup bundling in Obsidian plugins
34
+ - SubTabs do not manage DOM ownership beyond their render call
35
+ - Settings persistence handled via `plugin.saveSettings()`
36
+ - No global state or side effects introduced by the framework
37
+
38
+ [TOP](#changelog) [LICENSE](LICENSE) [ROADMAP](ROADMAP.md) [README](README.md)
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Dirk Brenckmann, db-developer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,233 @@
1
+ [![npm version](https://img.shields.io/npm/v/ts-obsidian-ui-settings?color=blue)](https://www.npmjs.com/package/ts-obsidian-ui-settings)
2
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
3
+ [![jsdoc](https://img.shields.io/static/v1?label=jsdoc&message=%20api%20&color=blue)](https://jsdoc.app/)
4
+ ![Build & Test](https://github.com/db-developer/ts-obsidian-ui-settings/actions/workflows/ci.yml/badge.svg)
5
+ [![codecov](https://codecov.io/gh/db-developer/ts-obsidian-ui-settings/branch/master/graph/badge.svg)](https://codecov.io/gh/db-developer/ts-obsidian-ui-settings)
6
+
7
+ [BOTTOM](#pluginsettingssubtabidmap--pluginsettingssubtabidttabids) [CHANGELOG](CHANGELOG.md) [LICENSE](LICENSE) [ROADMAP](ROADMAP.md)
8
+
9
+ # ts-obsidian-ui-settings
10
+
11
+ This module provides a **type-safe, extensible, and declarative framework** for building plugin settings in Obsidian with multiple sub-tabs. It allows separating rendering, state, and navigation responsibilities while keeping each sub-tab isolated and reusable.
12
+
13
+ ---
14
+
15
+ ## Motivation
16
+
17
+ Obsidian plugins run in a constrained environment:
18
+
19
+ - External runtime dependencies are discouraged
20
+ - `node_modules` are typically not shipped with plugins
21
+ - All code should be bundled into the final plugin file
22
+
23
+ This module is therefore designed to:
24
+
25
+ - Provide centralized navigation and active tab management
26
+ - SubTabs are **isolated** and **reusable** across plugins
27
+ - Full type safety with compile-time enforcement of SubTabIDs
28
+ - Easy to extend by adding new SubTab classes and updating the registry
29
+ - Integrate cleanly with Obsidian’s plugin environment
30
+ - Be **bundled (e.g. via Rollup)** directly into the plugin output
31
+
32
+ ---
33
+
34
+ ## Features
35
+
36
+ - Strongly typed sub-tab identifiers
37
+ - Compile-time enforcement of unique IDs
38
+ - Declarative sub-tab registry
39
+ - Decoupled render context for each sub-tab
40
+ - Automatic navigation and active-state management
41
+ - Seamlessly works with [ts-obsidian-ui-settings](https://github.com/db-developer/ts-obsidian-ui-settings) settings management
42
+ - Designed for **TypeScript** and **Rollup bundling** in Obsidian plugins
43
+ - SubTabs **do not manage DOM ownership** beyond the render call
44
+ - Settings persistence is always handled via `plugin.saveSettings()`
45
+ - No global state or side effects are introduced by this framework
46
+
47
+ [Details on AI assistance during development](AI.md)
48
+
49
+ ---
50
+
51
+ ## Usage
52
+
53
+ ### 1. Define SubTab IDs
54
+
55
+ Use module augmentation to declare all valid sub-tab IDs:
56
+
57
+ ```ts
58
+ // In a plugin-specific module (e.g., "my-plugin/subtabs")
59
+ declare module 'my-plugin/subtabs' {
60
+ interface PluginSettingsSubTabIdMap {
61
+ 'general': true;
62
+ 'structure': true;
63
+ 'ribbons': true;
64
+ }
65
+ }
66
+ ```
67
+
68
+ ### 2. Implement SubTabs
69
+
70
+ Each sub-tab must implement the `PluginSettingsSubTab<TSettings>` interface:
71
+
72
+ ```ts
73
+ import type { PluginSettingsSubTab, PluginSettingsRenderContext } from "ts-obsidian-ui-settings";
74
+
75
+ export class GeneralSubTab implements PluginSettingsSubTab<MySettings> {
76
+ header = 'General';
77
+
78
+ render(context: PluginSettingsRenderContext<MySettings>) {
79
+ const { containerEl, settings, saveSettings } = context;
80
+ containerEl.empty();
81
+ const el = containerEl.createEl('div', { text: `Current value: ${settings.someOption}` });
82
+
83
+ // Example input
84
+ const input = el.createEl('input', { type: 'text', value: settings.someOption });
85
+ input.addEventListener('change', (e) => {
86
+ settings.someOption = (e.target as HTMLInputElement).value;
87
+ saveSettings();
88
+ });
89
+ }
90
+ }
91
+ ```
92
+
93
+ ### 3. Create the SubTab Registry
94
+
95
+ ```ts
96
+ import type { PluginSettingsSubTabRegistry } from "ts-obsidian-ui-settings";
97
+
98
+ const subTabRegistry: PluginSettingsSubTabRegistry<MySettings, MyPluginSubTabIdMap> = {
99
+ general: new GeneralSubTab(),
100
+ structure: new StructureSubTab(),
101
+ ribbons: new RibbonsSubTab(),
102
+ };
103
+ ```
104
+
105
+ ### 4. Implement the Settings Tab Controller
106
+
107
+ ```ts
108
+ import { PluginSettingsTabWithSubTabs } from "ts-obsidian-ui-settings";
109
+
110
+ export class MyPluginSettingsTab extends PluginSettingsTabWithSubTabs<
111
+ MySettings,
112
+ MyPluginSubTabIdMap
113
+ > {
114
+ constructor(app: App, plugin: PluginWithSettings<MySettings>) {
115
+ super(app, plugin, subTabRegistry, 'general');
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### 5. Register the Settings Tab in Obsidian
121
+
122
+ ```ts
123
+ this.addSettingTab(new MyPluginSettingsTab(this.app, this));
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Build & Test
129
+
130
+ The package uses **Rollup** for bundling and Vitest for testing.
131
+
132
+ ```bash
133
+ # Build
134
+ npm run build
135
+
136
+ # Run tests
137
+ npm run test
138
+
139
+ # Run tests in watch mode
140
+ npm run test:watch
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Bundling (Required)
146
+
147
+ Obsidian does not support loading external npm modules at runtime.
148
+ All dependencies must be bundled.
149
+
150
+ ### Rollup Example
151
+
152
+ ```js
153
+ import resolve from "@rollup/plugin-node-resolve";
154
+ import typescript from "@rollup/plugin-typescript";
155
+
156
+ export default {
157
+ input: "src/main.ts",
158
+ output: {
159
+ file: "dist/main.js",
160
+ format: "cjs",
161
+ },
162
+ plugins: [
163
+ resolve(),
164
+ typescript(),
165
+ ],
166
+ external: ["obsidian"],
167
+ };
168
+ ```
169
+
170
+ ---
171
+
172
+ ## API Reference
173
+
174
+ ### `PluginSettingsTabWithSubTabs<TSettings, TTabIds>`
175
+
176
+ Abstract controller class that manages multiple sub-tabs in a plugin settings tab.
177
+
178
+ **Constructor:**
179
+ ```ts
180
+ // using import { App } from 'obsidian'
181
+ // using import { PluginWithSettings, PluginSettingsSubTabRegistry, PluginSettingsSubTabId} from 'ts-obsidian-ui-settings'
182
+ constructor(
183
+ app: App,
184
+ plugin: PluginWithSettings<TSettings>,
185
+ subTabs: PluginSettingsSubTabRegistry<TSettings, TTabIds>,
186
+ defaultSubTabId: PluginSettingsSubTabId<TTabIds>
187
+ )
188
+ ```
189
+
190
+ **Properties:**
191
+ - `subTabs` – registry of all available sub-tabs
192
+ - `activeSubTabId` – currently active sub-tab identifier
193
+
194
+ **Methods:**
195
+ - `display()` – renders the navigation and the active sub-tab
196
+ - `renderNavigation(containerEl: HTMLElement)` – renders tab buttons
197
+ - `renderActiveSubTab(containerEl: HTMLElement)` – renders the currently active sub-tab
198
+
199
+ ### `PluginSettingsSubTab<TSettings>`
200
+
201
+ Interface that each sub-tab must implement.
202
+
203
+ **Properties:**
204
+ - `header: string` – the title displayed in the navigation
205
+
206
+ **Methods:**
207
+ - `render(context: PluginSettingsRenderContext<TSettings>): void` – renders the sub-tab content into the given container
208
+
209
+ ### `PluginSettingsRenderContext<TSettings>`
210
+
211
+ Provides a decoupled rendering context for sub-tabs.
212
+
213
+ **Properties:**
214
+ - `containerEl: HTMLElement` – DOM container for rendering
215
+ - `settings: TSettings` – plugin settings object
216
+ - `saveSettings(): Promise<void>` – triggers settings persistence
217
+ - `plugin: PluginWithSettings<TSettings>` – plugin reference
218
+
219
+ ### `PluginSettingsSubTabRegistry<TSettings, TTabIds>`
220
+
221
+ A declarative mapping from sub-tab IDs to sub-tab instances.
222
+
223
+ ```ts
224
+ type PluginSettingsSubTabRegistry<TSettings, TTabIds extends PluginSettingsSubTabIdMap> = {
225
+ [K in PluginSettingsSubTabId<TTabIds>]: PluginSettingsSubTab<TSettings>;
226
+ };
227
+ ```
228
+
229
+ ### `PluginSettingsSubTabIdMap` / `PluginSettingsSubTabId<TTabIds>`
230
+
231
+ Compile-time registry and union type of valid sub-tab identifiers using module augmentation.
232
+
233
+ [TOP](#ts-obsidian-ui-settings) [CHANGELOG](CHANGELOG.md) [LICENSE](LICENSE) [ROADMAP](ROADMAP.md)
package/ROADMAP.md ADDED
@@ -0,0 +1,44 @@
1
+ [BOTTOM](#notes) [CHANGELOG](CHANGELOG.md) [LICENSE](LICENSE) [README](README.md)
2
+
3
+ # Roadmap
4
+
5
+ This roadmap outlines planned features, improvements, and long-term goals for the **ts-obsidian-ui-settings** framework.
6
+
7
+ ---
8
+
9
+ ## Short-term Goals (Next Release Cycle)
10
+
11
+ - Complete unit test coverage for all core classes and types
12
+ - Add examples for complex sub-tab interactions
13
+ - Improve documentation and API reference
14
+ - Enhance TypeScript type safety for nested sub-tabs and settings
15
+ - Introduce helper utilities for common sub-tab patterns
16
+
17
+ ---
18
+
19
+ ## Mid-term Goals
20
+
21
+ - Provide built-in support for dynamic sub-tab loading
22
+ - Implement optional persistent tab state across plugin reloads
23
+ - Offer theming options for tab navigation buttons
24
+ - Add more examples and templates for plugin developers
25
+
26
+ ---
27
+
28
+ ## Long-term Goals
29
+
30
+ - Support fully pluggable sub-tab layouts
31
+ - Integrate with other Obsidian plugin ecosystems
32
+ - Provide a CLI or generator to scaffold settings tabs
33
+ - Enable advanced type-level validation for plugin settings and dependencies
34
+ - Explore support for internationalized sub-tab headers and dynamic translations
35
+
36
+ ---
37
+
38
+ ## Notes
39
+
40
+ - All development will maintain full TypeScript compatibility.
41
+ - No external runtime dependencies will be introduced, to remain fully bundleable in Obsidian plugins.
42
+ - Sub-tabs will remain isolated and reusable across plugins.
43
+
44
+ [TOP](#roadmap) [CHANGELOG](CHANGELOG.md) [LICENSE](LICENSE) [README](README.md)
package/index.d.ts ADDED
@@ -0,0 +1,242 @@
1
+ import { PluginWithSettings } from 'ts-obsidian-plugin';
2
+ import { PluginSettingTab, Plugin, App } from 'obsidian';
3
+
4
+ /**
5
+ * Provides all dependencies required by a plugin settings sub-tab
6
+ * to render itself without owning navigation, lifecycle, or state
7
+ * management responsibilities.
8
+ *
9
+ * This context intentionally decouples rendering logic from the
10
+ * concrete PluginSettingTab implementation and avoids implicit
11
+ * access to global plugin state.
12
+ *
13
+ * @template TSettings
14
+ * The concrete settings type used by the plugin.
15
+ */
16
+ interface PluginSettingsRenderContext<TSettings extends object> {
17
+ /**
18
+ * The DOM element into which the sub-tab must render its UI.
19
+ *
20
+ * This container is guaranteed to be empty before rendering
21
+ * and is owned by the parent settings tab controller.
22
+ */
23
+ readonly containerEl: HTMLElement;
24
+ /**
25
+ * The current plugin settings instance.
26
+ *
27
+ * This object is mutable; however, persistence must be triggered
28
+ * explicitly via {@link saveSettings}.
29
+ */
30
+ readonly settings: TSettings;
31
+ /**
32
+ * Persists the current settings state.
33
+ *
34
+ * Sub-tabs must call this method after mutating settings in order
35
+ * to ensure durability across reloads.
36
+ */
37
+ saveSettings(): Promise<void>;
38
+ /**
39
+ * Reference to the hosting plugin instance.
40
+ *
41
+ * The exposed type is intentionally restricted to
42
+ * {@link PluginWithSettings} in order to prevent sub-tabs
43
+ * from relying on concrete plugin implementations.
44
+ */
45
+ readonly plugin: PluginWithSettings<TSettings>;
46
+ }
47
+
48
+ /**
49
+ * Contract implemented by a single settings sub-tab.
50
+ *
51
+ * A sub-tab represents an isolated, self-contained rendering unit
52
+ * within a plugin settings page. It must not manage navigation,
53
+ * active state, or DOM cleanup outside of its assigned container.
54
+ *
55
+ * @template TSettings
56
+ * The concrete settings type used by the plugin.
57
+ */
58
+ interface PluginSettingsSubTab<TSettings extends object> {
59
+ /**
60
+ * Human-readable tab header text.
61
+ *
62
+ * This value is typically derived from an i18n service and is
63
+ * expected to be stable for the lifetime of the tab instance.
64
+ */
65
+ get header(): string;
66
+ /**
67
+ * Renders the complete UI for this sub-tab.
68
+ *
69
+ * Implementations must assume that the provided container is empty
70
+ * and must not retain references to DOM elements beyond this call.
71
+ *
72
+ * @param ctx
73
+ * Rendering context providing access to settings, persistence,
74
+ * and plugin services.
75
+ */
76
+ render(ctx: PluginSettingsRenderContext<TSettings>): void;
77
+ }
78
+
79
+ /**
80
+ * Compile-time registry for all valid plugin settings sub-tab identifiers.
81
+ *
82
+ * This interface is intentionally empty and must be augmented via
83
+ * TypeScript module augmentation by consumers.
84
+ *
85
+ * The registry has no runtime representation and exists solely
86
+ * to enforce compile-time correctness and uniqueness of sub-tab IDs.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * // In a plugin-specific module (e.g. "my-plugin/subtabs")
91
+ * declare module "my-plugin/subtabs" {
92
+ * interface PluginSettingsSubTabIdMap {
93
+ * "general": true;
94
+ * "structure": true;
95
+ * "ribbons": true;
96
+ * }
97
+ * }
98
+ *
99
+ * type SubTabId = keyof PluginSettingsSubTabIdMap;
100
+ * // "general" | "structure" | "ribbons"
101
+ * ```
102
+ */
103
+ interface PluginSettingsSubTabIdMap {
104
+ }
105
+
106
+ /**
107
+ * Union type of all registered plugin settings sub-tab identifiers.
108
+ *
109
+ * This type represents the complete set of valid sub-tab IDs that
110
+ * may be used for navigation and persistence.
111
+ */
112
+ type PluginSettingsSubTabId<T extends PluginSettingsSubTabIdMap> = keyof T;
113
+
114
+ /**
115
+ * Declarative registry of settings sub-tabs.
116
+ *
117
+ * The keys of this registry are strongly typed sub-tab identifiers,
118
+ * guaranteed at compile time to be unique and valid.
119
+ *
120
+ * The registry is purely declarative and contains no UI state.
121
+ * Navigation, active state, and rendering orchestration are handled
122
+ * by the hosting settings tab controller.
123
+ *
124
+ * @template TSettings
125
+ * The concrete settings type used by the plugin.
126
+ *
127
+ * @template TTabIds
128
+ * The compile-time map of valid sub-tab identifiers, typically
129
+ * provided via TypeScript module augmentation.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * // 1. Declare valid sub-tab identifiers via module augmentation
134
+ * declare module "my-plugin/subtabs" {
135
+ * interface PluginSettingsSubTabIdMap {
136
+ * "general": true;
137
+ * "structure": true;
138
+ * "ribbons": true;
139
+ * }
140
+ * }
141
+ *
142
+ * // 2. Derive the union type of valid sub-tab identifiers
143
+ * type SubTabId = keyof PluginSettingsSubTabIdMap;
144
+ * // → "general" | "structure" | "ribbons"
145
+ *
146
+ * // 3. Define a registry mapping each sub-tab ID to its implementation
147
+ * const subTabs: PluginSettingsSubTabRegistry<
148
+ * MyPluginSettings,
149
+ * PluginSettingsSubTabIdMap
150
+ * > = {
151
+ * general: new GeneralSettingsSubTab(),
152
+ * structure: new StructureSettingsSubTab(),
153
+ * ribbons: new RibbonSettingsSubTab(),
154
+ * };
155
+ *
156
+ * // Any missing, misspelled, or duplicate key would result
157
+ * // in a compile-time error.
158
+ * ```
159
+ */
160
+ type PluginSettingsSubTabRegistry<TSettings, TTabIds extends PluginSettingsSubTabIdMap> = {
161
+ [K in PluginSettingsSubTabId<TTabIds>]: PluginSettingsSubTab<TSettings>;
162
+ };
163
+
164
+ /**
165
+ * Abstract settings tab controller with support for strongly typed sub-tabs.
166
+ *
167
+ * This class implements a declarative, extensible, and type-safe mechanism
168
+ * for rendering plugin settings using multiple sub-tabs.
169
+ *
170
+ * Responsibilities:
171
+ * - Rendering the navigation UI for sub-tabs
172
+ * - Managing the active sub-tab state
173
+ * - Providing a stable render context to sub-tabs
174
+ * - Orchestrating re-rendering on tab switches
175
+ *
176
+ * Non-responsibilities:
177
+ * - Rendering individual settings controls
178
+ * - Owning sub-tab DOM elements beyond the active container
179
+ * - Persisting settings directly (delegated to the plugin)
180
+ *
181
+ * @template TSettings
182
+ * The concrete settings object type used by the plugin.
183
+ *
184
+ * @template TTabIds
185
+ * The compile-time registry of valid sub-tab identifiers.
186
+ */
187
+ declare abstract class PluginSettingsTabWithSubTabs<TSettings extends object, TTabIds extends PluginSettingsSubTabIdMap> extends PluginSettingTab {
188
+ protected readonly plugin: Plugin & PluginWithSettings<TSettings>;
189
+ /**
190
+ * Declarative registry of all available sub-tabs.
191
+ *
192
+ * The keys of this registry are compile-time validated sub-tab
193
+ * identifiers, ensuring correctness and uniqueness.
194
+ */
195
+ protected readonly subTabs: PluginSettingsSubTabRegistry<TSettings, TTabIds>;
196
+ /**
197
+ * The currently active sub-tab identifier.
198
+ *
199
+ * This state is fully owned by the controller and never exposed
200
+ * to individual sub-tabs.
201
+ */
202
+ protected activeSubTabId: PluginSettingsSubTabId<TTabIds>;
203
+ /**
204
+ * Protected constructor for a settings tab with multiple sub-tabs.
205
+ *
206
+ * This constructor initializes the navigation and active tab state.
207
+ *
208
+ * @template TSettings The concrete plugin settings type.
209
+ * @template TTabIds The compile-time map of valid sub-tab identifiers.
210
+ *
211
+ * @param app The Obsidian application instance.
212
+ * @param plugin The hosting plugin instance, implementing both
213
+ * `Plugin` and `PluginWithSettings<TSettings>`.
214
+ * @param subTabs A declarative registry of all sub-tabs.
215
+ * @param defaultSubTabId The default sub-tab to activate on display.
216
+ */
217
+ protected constructor(app: App, plugin: Plugin & PluginWithSettings<TSettings>, subTabs: PluginSettingsSubTabRegistry<TSettings, TTabIds>, defaultSubTabId: PluginSettingsSubTabId<TTabIds>);
218
+ /**
219
+ * Called by Obsidian when the settings tab should be rendered.
220
+ *
221
+ * This method is final and must not be overridden by subclasses.
222
+ * Customization is achieved exclusively through sub-tab registration.
223
+ */
224
+ display(): void;
225
+ /**
226
+ * Renders the navigation UI for all registered sub-tabs.
227
+ *
228
+ * @param containerEl
229
+ * The container element used to host the navigation controls.
230
+ */
231
+ protected renderNavigation(containerEl: HTMLElement): void;
232
+ /**
233
+ * Renders the currently active sub-tab.
234
+ *
235
+ * @param containerEl
236
+ * The container element dedicated to sub-tab content.
237
+ */
238
+ protected renderActiveSubTab(containerEl: HTMLElement): void;
239
+ }
240
+
241
+ export { PluginSettingsTabWithSubTabs };
242
+ export type { PluginSettingsRenderContext, PluginSettingsSubTab, PluginSettingsSubTabId, PluginSettingsSubTabIdMap, PluginSettingsSubTabRegistry };
package/index.js ADDED
@@ -0,0 +1,52 @@
1
+ import { PluginSettingTab } from "obsidian";
2
+
3
+ class PluginSettingsTabWithSubTabs extends PluginSettingTab {
4
+ plugin;
5
+ subTabs;
6
+ activeSubTabId;
7
+ constructor(app, plugin, subTabs, defaultSubTabId) {
8
+ super(app, plugin);
9
+ this.plugin = plugin;
10
+ this.subTabs = subTabs;
11
+ this.activeSubTabId = defaultSubTabId;
12
+ }
13
+ display() {
14
+ this.containerEl.empty();
15
+ const navigationEl = this.containerEl.createDiv({
16
+ cls: "plugin-settings-subtab-nav"
17
+ });
18
+ const contentEl = this.containerEl.createDiv({
19
+ cls: "plugin-settings-subtab-content"
20
+ });
21
+ this.renderNavigation(navigationEl);
22
+ this.renderActiveSubTab(contentEl);
23
+ }
24
+ renderNavigation(containerEl) {
25
+ Object.keys(this.subTabs).forEach(tabId => {
26
+ const subTab = this.subTabs[tabId];
27
+ const button = containerEl.createEl("button", {
28
+ text: subTab.header,
29
+ cls: tabId === this.activeSubTabId ? "is-active" : undefined
30
+ });
31
+ button.addEventListener("click", () => {
32
+ if (this.activeSubTabId !== tabId) {
33
+ this.activeSubTabId = tabId;
34
+ this.display();
35
+ }
36
+ });
37
+ });
38
+ }
39
+ renderActiveSubTab(containerEl) {
40
+ const subTab = this.subTabs[this.activeSubTabId];
41
+ const context = {
42
+ containerEl: containerEl,
43
+ settings: this.plugin.settings,
44
+ saveSettings: () => this.plugin.saveSettings(),
45
+ plugin: this.plugin
46
+ };
47
+ subTab.render(context);
48
+ }
49
+ }
50
+
51
+ export { PluginSettingsTabWithSubTabs };
52
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzIjpbInNyYy9saWIvUGx1Z2luU2V0dGluZ3NUYWJXaXRoU3ViVGFicy50cyJdLCJuYW1lcyI6WyJQbHVnaW5TZXR0aW5nc1RhYldpdGhTdWJUYWJzIiwiUGx1Z2luU2V0dGluZ1RhYiIsInBsdWdpbiIsInN1YlRhYnMiLCJhY3RpdmVTdWJUYWJJZCIsImNvbnN0cnVjdG9yIiwiYXBwIiwiZGVmYXVsdFN1YlRhYklkIiwic3VwZXIiLCJ0aGlzIiwiZGlzcGxheSIsImNvbnRhaW5lckVsIiwiZW1wdHkiLCJuYXZpZ2F0aW9uRWwiLCJjcmVhdGVEaXYiLCJjbHMiLCJjb250ZW50RWwiLCJyZW5kZXJOYXZpZ2F0aW9uIiwicmVuZGVyQWN0aXZlU3ViVGFiIiwiT2JqZWN0Iiwia2V5cyIsImZvckVhY2giLCJ0YWJJZCIsInN1YlRhYiIsImJ1dHRvbiIsImNyZWF0ZUVsIiwidGV4dCIsImhlYWRlciIsInVuZGVmaW5lZCIsImFkZEV2ZW50TGlzdGVuZXIiLCJjb250ZXh0Iiwic2V0dGluZ3MiLCJzYXZlU2V0dGluZ3MiLCJyZW5kZXIiXSwibWFwcGluZ3MiOiI7O0FBaUNNLE1BQWdCQSxxQ0FHWkM7SUFvQ2FDO0lBNUJGQztJQVNUQztJQWlCVixXQUFBQyxDQUNFQyxLQUNtQkosUUFDbkJDLFNBQ0FJO1FBRUFDLE1BQU1GLEtBQUtKO1FBSlFPLEtBQUFQLFNBQUFBO1FBTW5CTyxLQUFLTixVQUFVQTtRQUNmTSxLQUFLTCxpQkFBaUJHO0FBQ3hCO0lBUVMsT0FBQUc7UUFDUEQsS0FBS0UsWUFBWUM7UUFFakIsTUFBTUMsZUFBZUosS0FBS0UsWUFBWUcsVUFBVTtZQUM5Q0MsS0FBSzs7UUFHUCxNQUFNQyxZQUFZUCxLQUFLRSxZQUFZRyxVQUFVO1lBQzNDQyxLQUFLOztRQUdQTixLQUFLUSxpQkFBaUJKO1FBQ3RCSixLQUFLUyxtQkFBbUJGO0FBQzFCO0lBUVUsZ0JBQUFDLENBQWlCTjtRQUN4QlEsT0FBT0MsS0FBS1gsS0FBS04sU0FFZmtCLFFBQVNDO1lBRVYsTUFBTUMsU0FBU2QsS0FBS04sUUFBUW1CO1lBRTVCLE1BQU1FLFNBQVNiLFlBQVljLFNBQVMsVUFBVTtnQkFDNUNDLE1BQU1ILE9BQU9JO2dCQUNiWixLQUFLTyxVQUFVYixLQUFLTCxpQkFDaEIsY0FDQXdCOztZQUdOSixPQUFPSyxpQkFBaUIsU0FBUztnQkFDL0IsSUFBSXBCLEtBQUtMLG1CQUFtQmtCLE9BQU87b0JBQ2pDYixLQUFLTCxpQkFBaUJrQjtvQkFDdEJiLEtBQUtDO0FBQ1A7OztBQUdOO0lBUVUsa0JBQUFRLENBQW1CUDtRQUMzQixNQUFNWSxTQUFTZCxLQUFLTixRQUFRTSxLQUFLTDtRQUVqQyxNQUFNMEIsVUFBa0Q7WUFDdERuQjtZQUNBb0IsVUFBVXRCLEtBQUtQLE9BQU82QjtZQUN0QkMsY0FBYyxNQUFNdkIsS0FBS1AsT0FBTzhCO1lBQ2hDOUIsUUFBUU8sS0FBS1A7O1FBR2ZxQixPQUFPVSxPQUFPSDtBQUNoQjs7OyJ9
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "ts-obsidian-ui-settings",
3
+ "version": "1.0.0",
4
+ "description": "Unified abstraction for obsidian setting ui with support für tabs",
5
+ "author": "Dirk Brenckmann <db.developer@gmx.de>",
6
+ "license": "MIT",
7
+ "main": "index.js",
8
+ "module": "index.js",
9
+ "types": "index.d.ts",
10
+ "type": "module",
11
+ "sideEffects": false,
12
+ "exports": {
13
+ ".": {
14
+ "import": "./index.js",
15
+ "types": "./index.d.ts"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "build": "rollup --config .config/rollup.config.mjs --environment BUILD:production",
20
+ "coverage": "vitest run --coverage --config .config/vitest.config.ts",
21
+ "package": "npm run build && npm pack",
22
+ "test": "vitest --config .config/vitest.config.ts",
23
+ "test:watch": "vitest --watch --config .config/vitest.config.ts",
24
+ "test:ui": "vitest --ui --config .config/vitest.config.ts"
25
+ },
26
+ "files": [
27
+ "index.js",
28
+ "index.d.ts",
29
+ "CHANGELOG.md",
30
+ "LICENSE",
31
+ "README.md",
32
+ "ROADMAP.md"
33
+ ],
34
+ "peerDependencies": {
35
+ "obsidian": ">=1.5.0"
36
+ },
37
+ "peerDependenciesMeta": {
38
+ "obsidian": {
39
+ "optional": false
40
+ }
41
+ },
42
+ "devDependencies": {
43
+ "@rollup/plugin-commonjs": "^29.0.0",
44
+ "@rollup/plugin-node-resolve": "^16.0.3",
45
+ "@rollup/plugin-terser": "^0.4.4",
46
+ "@rollup/plugin-typescript": "^12.3.0",
47
+ "@trivago/prettier-plugin-sort-imports": "^6.0.0",
48
+ "@types/node": "^24.10.0",
49
+ "@vitest/coverage-v8": "^4.0.15",
50
+ "@vitest/ui": "^4.0.15",
51
+ "jsdom": "^27.4.0",
52
+ "obsidian": "^1.10.2-1",
53
+ "prettier": "^3.7.4",
54
+ "prettier-plugin-align": "^0.0.1-alpha.1",
55
+ "rollup": "^4.53.1",
56
+ "rollup-plugin-dts": "^6.3.0",
57
+ "ts-obsidian-plugin": "^1.1.1",
58
+ "tslib": "^2.8.1",
59
+ "typescript": "^5.9.3",
60
+ "vitest": "^4.0.15"
61
+ }
62
+ }