Compare commits
13 Commits
1.0.0-alph
...
1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f82902b5e | |||
| d1eb545fed | |||
| a02ebb85d5 | |||
| c8014bd8c9 | |||
| cc4e71f920 | |||
| 175aebb218 | |||
| 52a5b4ce54 | |||
| 87d04ee834 | |||
| 3ecab8a9c6 | |||
| 9adc81705f | |||
| b52d2597f8 | |||
| 5b00626258 | |||
| 79c4af55d5 |
46
.github/workflows/release.yml
vendored
46
.github/workflows/release.yml
vendored
@@ -75,7 +75,8 @@ jobs:
|
|||||||
echo "File sizes:"
|
echo "File sizes:"
|
||||||
ls -lh main.js manifest.json styles.css
|
ls -lh main.js manifest.json styles.css
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release (GitHub)
|
||||||
|
if: github.server_url == 'https://github.com'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
@@ -106,3 +107,46 @@ jobs:
|
|||||||
|
|
||||||
echo "✅ Draft release created: $TAG_VERSION"
|
echo "✅ Draft release created: $TAG_VERSION"
|
||||||
echo "Visit https://github.com/${{ github.repository }}/releases to review and publish"
|
echo "Visit https://github.com/${{ github.repository }}/releases to review and publish"
|
||||||
|
|
||||||
|
- name: Create draft release (Gitea)
|
||||||
|
if: github.server_url != 'https://github.com'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
TAG_VERSION="${GITHUB_REF#refs/tags/}"
|
||||||
|
|
||||||
|
# Create release via API
|
||||||
|
RESPONSE=$(curl -s -X POST \
|
||||||
|
-H "Accept: application/json" \
|
||||||
|
-H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases" \
|
||||||
|
-d "$(cat <<EOF
|
||||||
|
{
|
||||||
|
"tag_name": "$TAG_VERSION",
|
||||||
|
"name": "$TAG_VERSION",
|
||||||
|
"body": "Release $TAG_VERSION\n\n## Changes\n\n*Add release notes here before publishing*\n\n## Installation\n\n1. Download main.js, manifest.json, and styles.css\n2. Create a folder in .obsidian/plugins/mcp-server/\n3. Copy the three files into the folder\n4. Reload Obsidian\n5. Enable the plugin in Settings → Community Plugins",
|
||||||
|
"draft": true,
|
||||||
|
"prerelease": false
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)")
|
||||||
|
|
||||||
|
# Extract release ID using grep and sed (no jq dependency)
|
||||||
|
RELEASE_ID=$(echo "$RESPONSE" | grep -o '"id":[0-9]*' | head -1 | grep -o '[0-9]*')
|
||||||
|
|
||||||
|
echo "Created release with ID: $RELEASE_ID"
|
||||||
|
|
||||||
|
# Upload release assets
|
||||||
|
for file in main.js manifest.json styles.css; do
|
||||||
|
echo "Uploading $file..."
|
||||||
|
curl -X POST \
|
||||||
|
-H "Accept: application/json" \
|
||||||
|
-H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary "@$file" \
|
||||||
|
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "✅ Draft release created: $TAG_VERSION"
|
||||||
|
echo "Visit ${{ github.server_url }}/${{ github.repository }}/releases to review and publish"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ The build command includes TypeScript type checking via `tsc -noEmit -skipLibChe
|
|||||||
|
|
||||||
### Installing in Obsidian
|
### Installing in Obsidian
|
||||||
After building, the plugin outputs `main.js` to the root directory. To test in Obsidian:
|
After building, the plugin outputs `main.js` to the root directory. To test in Obsidian:
|
||||||
1. Copy `main.js`, `manifest.json`, and `styles.css` to your vault's `.obsidian/plugins/obsidian-mcp-server/` directory
|
1. Copy `main.js`, `manifest.json`, and `styles.css` to your vault's `.obsidian/plugins/mcp-server/` directory
|
||||||
2. Reload Obsidian (Ctrl/Cmd + R in dev mode)
|
2. Reload Obsidian (Ctrl/Cmd + R in dev mode)
|
||||||
3. Enable the plugin in Settings → Community Plugins
|
3. Enable the plugin in Settings → Community Plugins
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Contributing to Obsidian MCP Server Plugin
|
# Contributing to MCP Server Plugin
|
||||||
|
|
||||||
Thank you for your interest in contributing to the Obsidian MCP Server Plugin! This document provides guidelines and information for contributors.
|
Thank you for your interest in contributing to the MCP Server Plugin! This document provides guidelines and information for contributors.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
@@ -64,12 +64,12 @@ For feature requests, please describe:
|
|||||||
|
|
||||||
**Linux/macOS:**
|
**Linux/macOS:**
|
||||||
```bash
|
```bash
|
||||||
ln -s /path/to/your/dev/obsidian-mcp-server /path/to/vault/.obsidian/plugins/obsidian-mcp-server
|
ln -s /path/to/your/dev/obsidian-mcp-server /path/to/vault/.obsidian/plugins/mcp-server
|
||||||
```
|
```
|
||||||
|
|
||||||
**Windows (Command Prompt as Administrator):**
|
**Windows (Command Prompt as Administrator):**
|
||||||
```cmd
|
```cmd
|
||||||
mklink /D "C:\path\to\vault\.obsidian\plugins\obsidian-mcp-server" "C:\path\to\your\dev\obsidian-mcp-server"
|
mklink /D "C:\path\to\vault\.obsidian\plugins\mcp-server" "C:\path\to\your\dev\obsidian-mcp-server"
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Start development build:**
|
4. **Start development build:**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Obsidian MCP Server Plugin
|
# MCP Server Plugin
|
||||||
|
|
||||||
An Obsidian plugin that makes your vault accessible via the [Model Context Protocol (MCP)](https://modelcontextprotocol.io) over HTTP. This allows AI assistants and other MCP clients to interact with your Obsidian vault programmatically.
|
A plugin that makes your vault accessible via the [Model Context Protocol (MCP)](https://modelcontextprotocol.io) over HTTP. This allows AI assistants and other MCP clients to interact with your vault programmatically.
|
||||||
|
|
||||||
**Version:** 1.0.0 | **Tested with:** Obsidian v1.9.14 | **License:** MIT
|
**Version:** 1.0.0 | **Tested with:** Obsidian v1.9.14 | **License:** MIT
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-mcp-server",
|
"id": "mcp-server",
|
||||||
"name": "MCP Server",
|
"name": "MCP Server",
|
||||||
"version": "1.0.0-alpha.2",
|
"version": "1.0.0",
|
||||||
"minAppVersion": "0.15.0",
|
"minAppVersion": "0.15.0",
|
||||||
"description": "Exposes Obsidian vault operations via Model Context Protocol (MCP) over HTTP.",
|
"description": "Exposes vault operations via Model Context Protocol (MCP) over HTTP.",
|
||||||
"author": "William Ballou",
|
"author": "William Ballou",
|
||||||
"isDesktopOnly": true,
|
"isDesktopOnly": true,
|
||||||
"fundingUrl": {
|
"fundingUrl": {
|
||||||
|
|||||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-mcp-server",
|
"name": "mcp-server",
|
||||||
"version": "1.0.0-alpha.2",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-mcp-server",
|
"name": "mcp-server",
|
||||||
"version": "1.0.0-alpha.2",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-mcp-server",
|
"name": "mcp-server",
|
||||||
"version": "1.0.0-alpha.2",
|
"version": "1.0.0",
|
||||||
"description": "MCP (Model Context Protocol) server plugin for Obsidian - exposes vault operations via HTTP",
|
"description": "MCP (Model Context Protocol) server plugin - exposes vault operations via HTTP",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
|
|||||||
146
src/settings.ts
146
src/settings.ts
@@ -6,6 +6,9 @@ import { generateApiKey } from './utils/auth-utils';
|
|||||||
export class MCPServerSettingTab extends PluginSettingTab {
|
export class MCPServerSettingTab extends PluginSettingTab {
|
||||||
plugin: MCPServerPlugin;
|
plugin: MCPServerPlugin;
|
||||||
private notificationDetailsEl: HTMLDetailsElement | null = null;
|
private notificationDetailsEl: HTMLDetailsElement | null = null;
|
||||||
|
private notificationToggleEl: HTMLElement | null = null;
|
||||||
|
private authDetailsEl: HTMLDetailsElement | null = null;
|
||||||
|
private configContainerEl: HTMLElement | null = null;
|
||||||
private activeConfigTab: 'windsurf' | 'claude-code' = 'windsurf';
|
private activeConfigTab: 'windsurf' | 'claude-code' = 'windsurf';
|
||||||
|
|
||||||
constructor(app: App, plugin: MCPServerPlugin) {
|
constructor(app: App, plugin: MCPServerPlugin) {
|
||||||
@@ -118,8 +121,11 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|||||||
|
|
||||||
containerEl.empty();
|
containerEl.empty();
|
||||||
|
|
||||||
// Clear notification details reference for fresh render
|
// Clear references for fresh render
|
||||||
this.notificationDetailsEl = null;
|
this.notificationDetailsEl = null;
|
||||||
|
this.notificationToggleEl = null;
|
||||||
|
this.authDetailsEl = null;
|
||||||
|
this.configContainerEl = null;
|
||||||
|
|
||||||
containerEl.createEl('h2', {text: 'MCP Server Settings'});
|
containerEl.createEl('h2', {text: 'MCP Server Settings'});
|
||||||
|
|
||||||
@@ -198,6 +204,9 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|||||||
authSummary.style.cursor = 'pointer';
|
authSummary.style.cursor = 'pointer';
|
||||||
authSummary.setText('Authentication & Configuration');
|
authSummary.setText('Authentication & Configuration');
|
||||||
|
|
||||||
|
// Store reference for targeted updates
|
||||||
|
this.authDetailsEl = authDetails;
|
||||||
|
|
||||||
// API Key Display (always show - auth is always enabled)
|
// API Key Display (always show - auth is always enabled)
|
||||||
new Setting(authDetails)
|
new Setting(authDetails)
|
||||||
.setName('API Key Management')
|
.setName('API Key Management')
|
||||||
@@ -254,6 +263,9 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|||||||
const configContainer = authDetails.createDiv({cls: 'mcp-config-snippet'});
|
const configContainer = authDetails.createDiv({cls: 'mcp-config-snippet'});
|
||||||
configContainer.style.marginBottom = '20px';
|
configContainer.style.marginBottom = '20px';
|
||||||
|
|
||||||
|
// Store reference for targeted updates
|
||||||
|
this.configContainerEl = configContainer;
|
||||||
|
|
||||||
// Tab buttons for switching between clients
|
// Tab buttons for switching between clients
|
||||||
const tabContainer = configContainer.createDiv({cls: 'mcp-config-tabs'});
|
const tabContainer = configContainer.createDiv({cls: 'mcp-config-tabs'});
|
||||||
tabContainer.style.display = 'flex';
|
tabContainer.style.display = 'flex';
|
||||||
@@ -273,7 +285,7 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|||||||
windsurfTab.style.fontWeight = this.activeConfigTab === 'windsurf' ? 'bold' : 'normal';
|
windsurfTab.style.fontWeight = this.activeConfigTab === 'windsurf' ? 'bold' : 'normal';
|
||||||
windsurfTab.addEventListener('click', () => {
|
windsurfTab.addEventListener('click', () => {
|
||||||
this.activeConfigTab = 'windsurf';
|
this.activeConfigTab = 'windsurf';
|
||||||
this.display();
|
this.updateConfigSection();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Claude Code tab button
|
// Claude Code tab button
|
||||||
@@ -288,7 +300,7 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|||||||
claudeCodeTab.style.fontWeight = this.activeConfigTab === 'claude-code' ? 'bold' : 'normal';
|
claudeCodeTab.style.fontWeight = this.activeConfigTab === 'claude-code' ? 'bold' : 'normal';
|
||||||
claudeCodeTab.addEventListener('click', () => {
|
claudeCodeTab.addEventListener('click', () => {
|
||||||
this.activeConfigTab = 'claude-code';
|
this.activeConfigTab = 'claude-code';
|
||||||
this.display();
|
this.updateConfigSection();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get configuration for active tab
|
// Get configuration for active tab
|
||||||
@@ -353,8 +365,11 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|||||||
// Store reference for targeted updates
|
// Store reference for targeted updates
|
||||||
this.notificationDetailsEl = notifDetails;
|
this.notificationDetailsEl = notifDetails;
|
||||||
|
|
||||||
// Enable notifications
|
// Enable notifications - create container for the toggle setting
|
||||||
new Setting(notifDetails)
|
const notificationToggleContainer = notifDetails.createDiv({cls: 'mcp-notification-toggle'});
|
||||||
|
this.notificationToggleEl = notificationToggleContainer;
|
||||||
|
|
||||||
|
new Setting(notificationToggleContainer)
|
||||||
.setName('Enable notifications')
|
.setName('Enable notifications')
|
||||||
.setDesc('Show when MCP tools are called')
|
.setDesc('Show when MCP tools are called')
|
||||||
.addToggle(toggle => toggle
|
.addToggle(toggle => toggle
|
||||||
@@ -376,7 +391,7 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|||||||
* Update only the notification section without re-rendering entire page
|
* Update only the notification section without re-rendering entire page
|
||||||
*/
|
*/
|
||||||
private updateNotificationSection(): void {
|
private updateNotificationSection(): void {
|
||||||
if (!this.notificationDetailsEl) {
|
if (!this.notificationDetailsEl || !this.notificationToggleEl) {
|
||||||
// Fallback to full re-render if reference lost
|
// Fallback to full re-render if reference lost
|
||||||
this.display();
|
this.display();
|
||||||
return;
|
return;
|
||||||
@@ -385,13 +400,16 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|||||||
// Store current open state
|
// Store current open state
|
||||||
const wasOpen = this.notificationDetailsEl.open;
|
const wasOpen = this.notificationDetailsEl.open;
|
||||||
|
|
||||||
// Find and remove all child elements except the summary
|
// Remove all children except the summary and the toggle container
|
||||||
const summary = this.notificationDetailsEl.querySelector('summary');
|
const summary = this.notificationDetailsEl.querySelector('summary');
|
||||||
while (this.notificationDetailsEl.lastChild && this.notificationDetailsEl.lastChild !== summary) {
|
const children = Array.from(this.notificationDetailsEl.children);
|
||||||
this.notificationDetailsEl.removeChild(this.notificationDetailsEl.lastChild);
|
for (const child of children) {
|
||||||
|
if (child !== summary && child !== this.notificationToggleEl) {
|
||||||
|
this.notificationDetailsEl.removeChild(child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rebuild notification settings
|
// Rebuild notification settings only if enabled
|
||||||
if (this.plugin.settings.notificationsEnabled) {
|
if (this.plugin.settings.notificationsEnabled) {
|
||||||
this.renderNotificationSettings(this.notificationDetailsEl);
|
this.renderNotificationSettings(this.notificationDetailsEl);
|
||||||
}
|
}
|
||||||
@@ -399,4 +417,112 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
|||||||
// Restore open state
|
// Restore open state
|
||||||
this.notificationDetailsEl.open = wasOpen;
|
this.notificationDetailsEl.open = wasOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update only the config section without re-rendering entire page
|
||||||
|
*/
|
||||||
|
private updateConfigSection(): void {
|
||||||
|
if (!this.configContainerEl) {
|
||||||
|
// Fallback to full re-render if reference lost
|
||||||
|
this.display();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store current open state of the auth details
|
||||||
|
const wasOpen = this.authDetailsEl?.open ?? false;
|
||||||
|
|
||||||
|
// Clear the config container
|
||||||
|
this.configContainerEl.empty();
|
||||||
|
|
||||||
|
// Tab buttons for switching between clients
|
||||||
|
const tabContainer = this.configContainerEl.createDiv({cls: 'mcp-config-tabs'});
|
||||||
|
tabContainer.style.display = 'flex';
|
||||||
|
tabContainer.style.gap = '8px';
|
||||||
|
tabContainer.style.marginBottom = '16px';
|
||||||
|
tabContainer.style.borderBottom = '1px solid var(--background-modifier-border)';
|
||||||
|
|
||||||
|
// Windsurf tab button
|
||||||
|
const windsurfTab = tabContainer.createEl('button', {text: 'Windsurf'});
|
||||||
|
windsurfTab.style.padding = '8px 16px';
|
||||||
|
windsurfTab.style.border = 'none';
|
||||||
|
windsurfTab.style.background = 'none';
|
||||||
|
windsurfTab.style.cursor = 'pointer';
|
||||||
|
windsurfTab.style.borderBottom = this.activeConfigTab === 'windsurf'
|
||||||
|
? '2px solid var(--interactive-accent)'
|
||||||
|
: '2px solid transparent';
|
||||||
|
windsurfTab.style.fontWeight = this.activeConfigTab === 'windsurf' ? 'bold' : 'normal';
|
||||||
|
windsurfTab.addEventListener('click', () => {
|
||||||
|
this.activeConfigTab = 'windsurf';
|
||||||
|
this.updateConfigSection();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Claude Code tab button
|
||||||
|
const claudeCodeTab = tabContainer.createEl('button', {text: 'Claude Code'});
|
||||||
|
claudeCodeTab.style.padding = '8px 16px';
|
||||||
|
claudeCodeTab.style.border = 'none';
|
||||||
|
claudeCodeTab.style.background = 'none';
|
||||||
|
claudeCodeTab.style.cursor = 'pointer';
|
||||||
|
claudeCodeTab.style.borderBottom = this.activeConfigTab === 'claude-code'
|
||||||
|
? '2px solid var(--interactive-accent)'
|
||||||
|
: '2px solid transparent';
|
||||||
|
claudeCodeTab.style.fontWeight = this.activeConfigTab === 'claude-code' ? 'bold' : 'normal';
|
||||||
|
claudeCodeTab.addEventListener('click', () => {
|
||||||
|
this.activeConfigTab = 'claude-code';
|
||||||
|
this.updateConfigSection();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get configuration for active tab
|
||||||
|
const {filePath, config, usageNote} = this.generateConfigForClient(this.activeConfigTab);
|
||||||
|
|
||||||
|
// Tab content area
|
||||||
|
const tabContent = this.configContainerEl.createDiv({cls: 'mcp-config-content'});
|
||||||
|
tabContent.style.marginTop = '16px';
|
||||||
|
|
||||||
|
// File location label
|
||||||
|
const fileLocationLabel = tabContent.createEl('p', {text: 'Configuration file location:'});
|
||||||
|
fileLocationLabel.style.marginBottom = '4px';
|
||||||
|
fileLocationLabel.style.fontSize = '0.9em';
|
||||||
|
fileLocationLabel.style.color = 'var(--text-muted)';
|
||||||
|
|
||||||
|
// File path display
|
||||||
|
const filePathDisplay = tabContent.createEl('div', {text: filePath});
|
||||||
|
filePathDisplay.style.padding = '8px';
|
||||||
|
filePathDisplay.style.backgroundColor = 'var(--background-secondary)';
|
||||||
|
filePathDisplay.style.borderRadius = '4px';
|
||||||
|
filePathDisplay.style.fontFamily = 'monospace';
|
||||||
|
filePathDisplay.style.fontSize = '0.9em';
|
||||||
|
filePathDisplay.style.marginBottom = '12px';
|
||||||
|
filePathDisplay.style.color = 'var(--text-muted)';
|
||||||
|
|
||||||
|
// Copy button
|
||||||
|
const copyConfigButton = tabContent.createEl('button', {text: '📋 Copy Configuration'});
|
||||||
|
copyConfigButton.style.marginBottom = '12px';
|
||||||
|
copyConfigButton.addEventListener('click', async () => {
|
||||||
|
await navigator.clipboard.writeText(JSON.stringify(config, null, 2));
|
||||||
|
new Notice('✅ Configuration copied to clipboard');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Config JSON display
|
||||||
|
const configDisplay = tabContent.createEl('pre');
|
||||||
|
configDisplay.style.padding = '12px';
|
||||||
|
configDisplay.style.backgroundColor = 'var(--background-secondary)';
|
||||||
|
configDisplay.style.borderRadius = '4px';
|
||||||
|
configDisplay.style.fontSize = '0.85em';
|
||||||
|
configDisplay.style.overflowX = 'auto';
|
||||||
|
configDisplay.style.userSelect = 'text';
|
||||||
|
configDisplay.style.cursor = 'text';
|
||||||
|
configDisplay.style.marginBottom = '12px';
|
||||||
|
configDisplay.textContent = JSON.stringify(config, null, 2);
|
||||||
|
|
||||||
|
// Usage note
|
||||||
|
const usageNoteDisplay = tabContent.createEl('p', {text: usageNote});
|
||||||
|
usageNoteDisplay.style.fontSize = '0.9em';
|
||||||
|
usageNoteDisplay.style.color = 'var(--text-muted)';
|
||||||
|
usageNoteDisplay.style.fontStyle = 'italic';
|
||||||
|
|
||||||
|
// Restore open state (only if authDetailsEl is available)
|
||||||
|
if (this.authDetailsEl) {
|
||||||
|
this.authDetailsEl.open = wasOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user