Browse Source

big-fixing, increased error logging, startup script, generate private key, implement third-party asciidoc plugin on install

master
Silberengel 1 week ago
parent
commit
4f7472bac1
  1. 1
      .gitignore
  2. 146
      README.md
  3. 129
      package-lock.json
  4. 2
      package.json
  5. 172
      src/main.ts
  6. 64
      src/ui/settingsTab.ts
  7. 556
      start-obsidian.sh

1
.gitignore vendored

@ -4,3 +4,4 @@ node_modules/
main.js main.js
main.js.map main.js.map
versions.json versions.json
.scriptorium-vault-path

146
README.md

@ -3,122 +3,100 @@
An Obsidian plugin for creating, editing, and publishing Nostr document events directly from your vault. An Obsidian plugin for creating, editing, and publishing Nostr document events directly from your vault.
**Author**: Silberengel **Author**: Silberengel
**Homepage**: https://gitcitadel.com **Homepage of our development project**: https://gitcitadel.com
**Funding**: gitcitadel@getalby.com **Happy to receive Bitcoin-tips to our Lightning wallet**: gitcitadel@getalby.com
## Features ## Features
- **Multiple Event Kinds**: Support for Markdown-formatted kinds (1, 11, 30023, 30817) and Asdciidoc-formatted kinds (30040, 30041, 30818). - Multiple event kinds: Markdown (1, 11, 30023, 30817) and AsciiDoc (30040, 30041, 30818) formats
- **Bookstr Support**: Automatic parsing and splitting of e-books/publications into nested 30040/30041 structures - Automatic book/publication parsing into nested 30040/30041 structures
- **Metadata Management**: YAML metadata files with validation per event kind - YAML metadata management with validation
- **Structure Preview**: Visual preview of publication structure before creating events - Structure preview before creating events
- **Two-Step Workflow**: Create and sign events separately from publishing - Two-step workflow: create/sign events separately from publishing
- **Relay Management**: Automatic fetching of relay lists (kind 10002) with AUTH support - Automatic relay list fetching (kind 10002) with AUTH support
- **d-tag Normalization**: Automatic NIP-54 compliant d-tag generation from titles - NIP-54 compliant d-tag normalization
## Installation ## Installation
### Manual Installation ### Quick Start
1. Clone this repository Use the startup script to build, install, and launch Obsidian:
2. Run `npm install`.obsidian/plugins/scriptorium-obsidian/
3. Run `npm run build`
4. Create the plugin directory in your Obsidian vault (if it doesn't exist):
- Navigate to your vault's root directory
- Create `.obsidian/plugins/scriptorium-obsidian/` directory
5. Copy the `main.js` and `manifest.json` files to `.obsidian/plugins/scriptorium-obsidian/`
6. Reload Obsidian and enable the plugin in Settings → Community Plugins
**Note**: The `.obsidian` folder is hidden by default. You may need to show hidden files in your file manager to see it. ```bash
# First run (path required)
./start-obsidian.sh ~/Documents/MyVault
## Setup # Subsequent runs (path is saved)
./start-obsidian.sh
```
### Private Key Configuration The script automatically:
- Installs npm dependencies
- Builds and installs the plugin
- Installs and enables obsidian-asciidoc (required for `.adoc` files)
- Starts Obsidian with console logging
You have three options to set your private key: **Generate a new Nostr key:**
```bash
./start-obsidian.sh --generate-key
```
Add the shown export command to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.).
**Option 1: Manual Entry (Easiest)** ### Manual Installation
1. Open Obsidian settings → Scriptorium Nostr
2. Enter your private key in the password field 1. Clone repo → `npm install``npm run build`
3. Click "Set Key" 2. Copy `main.js` and `manifest.json` to `.obsidian/plugins/scriptorium-obsidian/`
3. Enable in Obsidian Settings → Community Plugins
4. Install [obsidian-asciidoc](https://github.com/dzruyk/obsidian-asciidoc) plugin (required for `.adoc` files)
**Option 2: File in Vault (Most Reliable)** ## Setup
1. Create a file named `.scriptorium_key` in your vault root
2. Put your private key on a single line (nsec1... or 64-char hex)
3. Open plugin settings and click "Refresh"
**Option 3: Environment Variable** ### Private Key
1. Set `SCRIPTORIUM_OBSIDIAN_KEY` in your terminal:
```bash
export SCRIPTORIUM_OBSIDIAN_KEY="nsec1..."
```
2. **Important:** Launch Obsidian from the same terminal:
```bash
obsidian
```
(If Obsidian is already running, close it and restart from the terminal)
3. Open plugin settings → Scriptorium Nostr
4. Click "Refresh" to load your private key
**Note:** Environment variables are only available to processes launched from the terminal where they were set. If you launch Obsidian from a desktop shortcut or application menu, it won't have access to the environment variable. You must launch Obsidian from the terminal where you set the variable. The plugin only loads keys from the `SCRIPTORIUM_OBSIDIAN_KEY` environment variable:
```bash
# Set in terminal
export SCRIPTORIUM_OBSIDIAN_KEY="nsec1..."
See [ENV_SETUP.md](ENV_SETUP.md) for detailed instructions on setting environment variables. # Make permanent (add to ~/.bashrc or ~/.zshrc)
echo 'export SCRIPTORIUM_OBSIDIAN_KEY="nsec1..."' >> ~/.bashrc
```
**Key Format:** `nsec1...` (bech32) or 64-character hex string **Important:** Launch Obsidian from the terminal where the variable is set, or use the startup script. Desktop shortcuts won't have access to the variable.
### Relay Configuration ### Relays
1. Open Obsidian settings → Scriptorium Nostr 1. Open Settings → Scriptorium Nostr
2. Click "Fetch" to get your relay list from Nostr relays 2. Click "Fetch" to get your relay list from Nostr relays
3. The plugin will automatically fetch from `wss://profiles.nostr1.com`, `wss://relay.damus.io`, and `wss://thecitadel.nostr1.com` 3. Relays are automatically fetched from default relays
## Usage ## Usage
### Creating Events ### Creating Events
1. Open a Markdown or AsciiDoc file 1. Open a Markdown or AsciiDoc file
2. Run command: `Create Nostr Events` 2. Run: `Create Nostr Events`
3. If metadata doesn't exist, it will be created with defaults 3. Events are created, signed, and saved to `{filename}_events.jsonl`
4. For AsciiDoc documents with structure (`= Title`), a preview will be shown
5. Events are created, signed, and saved to `{filename}_events.jsonl`
### Editing Metadata
1. Open a file
2. Run command: `Edit Metadata`
3. Fill in the metadata form
4. Save
### Publishing Events ### Publishing
1. Ensure events have been created (check for `{filename}_events.jsonl`) 1. Ensure events exist (`{filename}_events.jsonl`)
2. Run command: `Publish Events to Relays` 2. Run: `Publish Events to Relays`
3. Events will be published to all configured write relays 3. Events publish to all configured write relays
### Previewing Structure ### Other Commands
1. Open an AsciiDoc file with structure - `Edit Metadata` - Open metadata form for current file
2. Run command: `Preview Document Structure` - `Preview Document Structure` - Show event hierarchy (AsciiDoc only)
3. Review the event hierarchy before creating
## File Formats ## File Formats
- **Markdown** (`.md`): Kinds 1, 11, 30023, 30817 - **Markdown** (`.md`): Kinds 1, 11, 30023, 30817
- **AsciiDoc** (`.adoc`, `.asciidoc`): Kinds 30041, 30818 - **AsciiDoc** (`.adoc`): Kinds 30040, 30041, 30818
- **AsciiDoc with Structure** (starts with `= Title`): Kind 30040 with nested 30041 events - **Structured AsciiDoc** (starts with `= Title`): Kind 30040 with nested 30041 events
## Metadata Files
Metadata is stored as `{filename}_metadata.yml` in the same directory as the document.
For 30040 events, the title is derived from the document header (`= Title`) but can be overridden in the metadata file. Metadata is stored as `{filename}_metadata.yml` in the same directory.
## Commands
- `Create Nostr Events` - Create and sign events from current file
- `Preview Document Structure` - Show event hierarchy preview
- `Publish Events to Relays` - Publish from .jsonl file to relays
- `Edit Metadata` - Open metadata form for current file
## Development ## Development
@ -131,9 +109,3 @@ npm run build # Production build
## License ## License
MIT MIT
## Author
**Silberengel**
- Homepage: https://gitcitadel.com
- Funding: gitcitadel@getalby.com

129
package-lock.json generated

@ -9,8 +9,6 @@
"version": "0.1.0", "version": "0.1.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@codemirror/lang-yaml": "^6.1.2",
"codemirror-asciidoc": "^2.0.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"nostr-tools": "^2.20.0" "nostr-tools": "^2.20.0"
}, },
@ -26,68 +24,6 @@
"typescript": "5.3.3" "typescript": "5.3.3"
} }
}, },
"node_modules/@codemirror/autocomplete": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz",
"integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0"
}
},
"node_modules/@codemirror/lang-yaml": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.2.tgz",
"integrity": "sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.2.0",
"@lezer/lr": "^1.0.0",
"@lezer/yaml": "^1.0.0"
}
},
"node_modules/@codemirror/language": {
"version": "6.12.1",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.1.tgz",
"integrity": "sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
"@lezer/common": "^1.5.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"style-mod": "^4.0.0"
}
},
"node_modules/@codemirror/state": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz",
"integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==",
"license": "MIT",
"dependencies": {
"@marijn/find-cluster-break": "^1.0.0"
}
},
"node_modules/@codemirror/view": {
"version": "6.39.11",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.11.tgz",
"integrity": "sha512-bWdeR8gWM87l4DB/kYSF9A+dVackzDb/V56Tq7QVrQ7rn86W0rgZFtlL3g3pem6AeGcb9NQNoy3ao4WpW4h5tQ==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.5.0",
"crelt": "^1.0.6",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.17.3", "version": "0.17.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz",
@ -462,47 +398,6 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/@lezer/common": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.0.tgz",
"integrity": "sha512-PNGcolp9hr4PJdXR4ix7XtixDrClScvtSCYW3rQG106oVMOOI+jFb+0+J3mbeL/53g1Zd6s0kJzaw6Ri68GmAA==",
"license": "MIT"
},
"node_modules/@lezer/highlight": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz",
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.3.0"
}
},
"node_modules/@lezer/lr": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.8.tgz",
"integrity": "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
"node_modules/@lezer/yaml": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.3.tgz",
"integrity": "sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.4.0"
}
},
"node_modules/@marijn/find-cluster-break": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
"license": "MIT"
},
"node_modules/@noble/ciphers": { "node_modules/@noble/ciphers": {
"version": "0.5.3", "version": "0.5.3",
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz",
@ -922,18 +817,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/codemirror-asciidoc": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/codemirror-asciidoc/-/codemirror-asciidoc-2.0.1.tgz",
"integrity": "sha512-h6Xhj+ZsWh/DTNE3xMfRv9edufchsVVwPED7wSGMeEdoYk/UtCZmwRGH0ZZQkr43aNVF3tWGLZJGT+cAeYgUIg==",
"license": "BSD"
},
"node_modules/crelt": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
"license": "MIT"
},
"node_modules/debug": { "node_modules/debug": {
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@ -1428,12 +1311,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/style-mod": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",
"license": "MIT"
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -1497,12 +1374,6 @@
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
},
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
"license": "MIT"
} }
} }
} }

2
package.json

@ -24,8 +24,6 @@
"typescript": "5.3.3" "typescript": "5.3.3"
}, },
"dependencies": { "dependencies": {
"@codemirror/lang-yaml": "^6.1.2",
"codemirror-asciidoc": "^2.0.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"nostr-tools": "^2.20.0" "nostr-tools": "^2.20.0"
} }

172
src/main.ts

@ -12,39 +12,26 @@ import { getWriteRelays } from "./relayManager";
import { parseAsciiDocStructure, isAsciiDocDocument } from "./asciidocParser"; import { parseAsciiDocStructure, isAsciiDocDocument } from "./asciidocParser";
import { normalizeSecretKey, getPubkeyFromPrivkey } from "./nostr/eventBuilder"; import { normalizeSecretKey, getPubkeyFromPrivkey } from "./nostr/eventBuilder";
import { safeConsoleError, safeConsoleLog, verifyEventSecurity } from "./utils/security"; import { safeConsoleError, safeConsoleLog, verifyEventSecurity } from "./utils/security";
// CodeMirror language packages for syntax highlighting
// These will be bundled with the plugin
import { yaml } from "@codemirror/lang-yaml";
import { asciidoc } from "codemirror-asciidoc";
import { StreamLanguage } from "@codemirror/language";
import { Extension } from "@codemirror/state";
import { EditorView } from "@codemirror/view";
export default class ScriptoriumPlugin extends Plugin { export default class ScriptoriumPlugin extends Plugin {
settings!: ScriptoriumSettings; settings!: ScriptoriumSettings;
async onload() { async onload() {
await this.loadSettings(); // Log to terminal console that started Obsidian
await this.loadPrivateKey(); console.error("[Scriptorium] Plugin loading...");
process.stderr.write("[Scriptorium] Plugin loading...\n");
// Register AsciiDoc file extensions so Obsidian can open them
// This tells Obsidian to treat .adoc and .asciidoc files as editable text files try {
// Using "text" view type so they open as plain text editors in Obsidian await this.loadSettings();
this.registerExtensions(["adoc", "asciidoc"], "text"); await this.loadPrivateKey();
// Register YAML file extensions so Obsidian can open metadata files // Note: We don't register file extensions for .yml, .yaml, .adoc, or .asciidoc files
// This tells Obsidian to treat .yml and .yaml files as editable text files // Users should install the obsidian-asciidoc plugin for .adoc file support
this.registerExtensions(["yml", "yaml"], "text"); // YAML files can be edited with external editors or Obsidian may handle them natively
console.error("[Scriptorium] Plugin loaded - file extensions not registered");
// Register editor extensions for syntax highlighting process.stderr.write("[Scriptorium] Plugin loaded - file extensions not registered\n");
// Apply YAML and AsciiDoc language modes console.error("[Scriptorium] Install obsidian-asciidoc plugin for .adoc file editing support");
// Note: Markdown files (.md) use Obsidian's default "markdown" view type process.stderr.write("[Scriptorium] Install obsidian-asciidoc plugin for .adoc file editing support\n");
// which has built-in syntax highlighting, so they won't be affected by these extensions
// These extensions only apply to files registered with "text" view type (.yml, .yaml, .adoc, .asciidoc)
this.registerEditorExtension([
yaml(),
StreamLanguage.define(asciidoc)
]);
// Add settings tab // Add settings tab
this.addSettingTab(new ScriptoriumSettingTab(this.app, this)); this.addSettingTab(new ScriptoriumSettingTab(this.app, this));
@ -85,8 +72,22 @@ export default class ScriptoriumPlugin extends Plugin {
this.handleNewDocument(); this.handleNewDocument();
}); });
// Status bar // Status bar
this.addStatusBarItem().setText("Scriptorium"); this.addStatusBarItem().setText("Scriptorium");
console.error("[Scriptorium] Plugin loaded successfully");
process.stderr.write("[Scriptorium] Plugin loaded successfully\n");
} catch (error: any) {
const errorMsg = error?.message || String(error);
const stackTrace = error?.stack || "";
console.error(`[Scriptorium] Error loading plugin: ${errorMsg}`);
process.stderr.write(`[Scriptorium] Error loading plugin: ${errorMsg}\n`);
if (stackTrace) {
console.error(`[Scriptorium] Stack trace:`, stackTrace);
process.stderr.write(`[Scriptorium] Stack trace: ${stackTrace}\n`);
}
safeConsoleError("Error loading plugin:", error);
}
} }
onunload() {} onunload() {}
@ -100,9 +101,7 @@ export default class ScriptoriumPlugin extends Plugin {
} }
async loadPrivateKey(): Promise<boolean> { async loadPrivateKey(): Promise<boolean> {
// Try multiple methods to load the private key // Load private key from environment variable only
// Method 1: Try environment variable (may not work in Obsidian's sandbox)
try { try {
// @ts-ignore - process.env may not be typed in Obsidian context // @ts-ignore - process.env may not be typed in Obsidian context
if (typeof process !== "undefined" && process.env?.SCRIPTORIUM_OBSIDIAN_KEY) { if (typeof process !== "undefined" && process.env?.SCRIPTORIUM_OBSIDIAN_KEY) {
@ -117,38 +116,6 @@ export default class ScriptoriumPlugin extends Plugin {
// Environment variable access not available // Environment variable access not available
} }
// Method 2: Try reading from a file in the vault (.scriptorium_key)
try {
const keyFile = this.app.vault.getAbstractFileByPath(".scriptorium_key");
if (keyFile && keyFile instanceof TFile) {
const keyContent = await this.app.vault.read(keyFile);
const key = keyContent.trim();
if (key && (key.startsWith("nsec1") || /^[0-9a-f]{64}$/i.test(key))) {
this.settings.privateKey = key;
await this.saveSettings();
return true;
}
}
} catch (error) {
// File doesn't exist or can't be read
}
// Method 3: Try reading from .obsidian/scriptorium_key (hidden file)
try {
const hiddenKeyFile = this.app.vault.getAbstractFileByPath(".obsidian/scriptorium_key");
if (hiddenKeyFile && hiddenKeyFile instanceof TFile) {
const keyContent = await this.app.vault.read(hiddenKeyFile);
const key = keyContent.trim();
if (key && (key.startsWith("nsec1") || /^[0-9a-f]{64}$/i.test(key))) {
this.settings.privateKey = key;
await this.saveSettings();
return true;
}
}
} catch (error) {
// File doesn't exist or can't be read
}
return false; return false;
} }
@ -406,6 +373,9 @@ export default class ScriptoriumPlugin extends Plugin {
private async handleNewDocument() { private async handleNewDocument() {
new NewDocumentModal(this.app, async (kind: EventKind, title: string) => { new NewDocumentModal(this.app, async (kind: EventKind, title: string) => {
try { try {
console.error(`[Scriptorium] Creating new document: kind=${kind}, title=${title}`);
process.stderr.write(`[Scriptorium] Creating new document: kind=${kind}, title=${title}\n`);
// Ensure folder structure exists // Ensure folder structure exists
const folderPath = await this.ensureNostrNotesFolder(kind); const folderPath = await this.ensureNostrNotesFolder(kind);
@ -453,17 +423,30 @@ export default class ScriptoriumPlugin extends Plugin {
let file: TFile; let file: TFile;
try { try {
console.error(`[Scriptorium] Creating file: ${filePath}`);
process.stderr.write(`[Scriptorium] Creating file: ${filePath}\n`);
file = await this.app.vault.create(filePath, content); file = await this.app.vault.create(filePath, content);
console.error(`[Scriptorium] File created successfully: ${file.path}`);
process.stderr.write(`[Scriptorium] File created successfully: ${file.path}\n`);
// Verify file was actually created // Verify file was actually created
const verifyFile = this.app.vault.getAbstractFileByPath(filePath); const verifyFile = this.app.vault.getAbstractFileByPath(filePath);
if (!verifyFile || !(verifyFile instanceof TFile)) { if (!verifyFile || !(verifyFile instanceof TFile)) {
new Notice(`Error: File ${filename} was not created properly`); const msg = `Error: File ${filename} was not created properly`;
console.error(`[Scriptorium] ${msg}`);
process.stderr.write(`[Scriptorium] ${msg}\n`);
new Notice(msg);
safeConsoleError(`File creation verification failed for ${filePath}`); safeConsoleError(`File creation verification failed for ${filePath}`);
return; return;
} }
} catch (error: any) { } catch (error: any) {
const safeMessage = error?.message ? String(error.message).replace(/nsec1[a-z0-9]{58,}/gi, "[REDACTED]").replace(/[0-9a-f]{64}/gi, "[REDACTED]") : "Unknown error"; const safeMessage = error?.message ? String(error.message).replace(/nsec1[a-z0-9]{58,}/gi, "[REDACTED]").replace(/[0-9a-f]{64}/gi, "[REDACTED]") : "Unknown error";
console.error(`[Scriptorium] Error creating file: ${safeMessage}`);
process.stderr.write(`[Scriptorium] Error creating file: ${safeMessage}\n`);
if (error?.stack) {
console.error(`[Scriptorium] Stack trace:`, error.stack);
process.stderr.write(`[Scriptorium] Stack trace: ${error.stack}\n`);
}
new Notice(`Error creating file: ${safeMessage}`); new Notice(`Error creating file: ${safeMessage}`);
safeConsoleError("Error creating file:", error); safeConsoleError("Error creating file:", error);
safeConsoleError("File path was:", filePath); safeConsoleError("File path was:", filePath);
@ -485,19 +468,52 @@ export default class ScriptoriumPlugin extends Plugin {
// Continue anyway - file was created // Continue anyway - file was created
} }
// Open the new file in Obsidian workspace (use active leaf or create new) // For .adoc files, ensure file is visible in explorer but don't auto-open
try { if (file.extension === "adoc" || file.extension === "asciidoc") {
const leaf = this.app.workspace.getMostRecentLeaf(); console.error(`[Scriptorium] AsciiDoc file created: ${file.path}`);
if (leaf) { process.stderr.write(`[Scriptorium] AsciiDoc file created: ${file.path}\n`);
await leaf.openFile(file);
} else { // File should be visible in Obsidian's file explorer automatically
// Fallback: open in new leaf // since we used vault.create(). The file explorer will refresh automatically.
const newLeaf = this.app.workspace.getLeaf("tab"); // We don't auto-open to prevent crashes (obsidian-asciidoc plugin handles opening)
await newLeaf.openFile(file);
new Notice(`Created ${filename} in ${folderPath}. Install obsidian-asciidoc plugin to edit in Obsidian.`);
} else {
// Open the new file in Obsidian workspace (use active leaf or create new)
// Use a small delay to ensure file is fully created before opening
console.error(`[Scriptorium] Waiting before opening file...`);
process.stderr.write(`[Scriptorium] Waiting before opening file...\n`);
await new Promise(resolve => setTimeout(resolve, 200));
try {
console.error(`[Scriptorium] Attempting to open file: ${file.path} (extension: ${file.extension})`);
process.stderr.write(`[Scriptorium] Attempting to open file: ${file.path} (extension: ${file.extension})\n`);
const leaf = this.app.workspace.getMostRecentLeaf();
if (leaf && leaf.view) {
await leaf.openFile(file, { active: true });
console.error(`[Scriptorium] File opened successfully in existing leaf`);
process.stderr.write(`[Scriptorium] File opened successfully in existing leaf\n`);
} else {
// Fallback: open in new leaf
const newLeaf = this.app.workspace.getLeaf("tab");
await newLeaf.openFile(file, { active: true });
console.error(`[Scriptorium] File opened successfully in new leaf`);
process.stderr.write(`[Scriptorium] File opened successfully in new leaf\n`);
}
} catch (error: any) {
const errorMsg = error?.message || String(error);
const stackTrace = error?.stack || "";
console.error(`[Scriptorium] Error opening file: ${errorMsg}`);
process.stderr.write(`[Scriptorium] Error opening file: ${errorMsg}\n`);
if (stackTrace) {
console.error(`[Scriptorium] Stack trace:`, stackTrace);
process.stderr.write(`[Scriptorium] Stack trace: ${stackTrace}\n`);
}
safeConsoleError("Error opening file:", error);
// File was created, just couldn't open it - show a notice
new Notice(`File created but couldn't open: ${file.name}`);
} }
} catch (error: any) {
safeConsoleError("Error opening file:", error);
// File was created, just couldn't open it
} }
new Notice(`Created ${filename} in ${folderPath}`); new Notice(`Created ${filename} in ${folderPath}`);

64
src/ui/settingsTab.ts

@ -71,40 +71,14 @@ export class ScriptoriumSettingTab extends PluginSettingTab {
.setCta() .setCta()
.onClick(async () => { .onClick(async () => {
const loaded = await this.plugin.loadPrivateKey(); const loaded = await this.plugin.loadPrivateKey();
if (!loaded && !this.plugin.settings.privateKey) { if (loaded) {
new Notice("Could not load private key. Please enter it manually below."); new Notice("Private key loaded from environment variable");
} else {
new Notice("Could not load private key from SCRIPTORIUM_OBSIDIAN_KEY environment variable");
} }
await this.display(); await this.display();
}); });
}); });
// Allow manual update of private key
new Setting(containerEl)
.setName("Update Private Key")
.setDesc("Manually enter or update your private key (nsec1... or 64-char hex). Leave empty to keep current.")
.addText((text) => {
text.setPlaceholder("nsec1... or hex key")
.setValue("")
.inputEl.type = "password";
})
.addButton((button) => {
button.setButtonText("Update")
.onClick(async () => {
const input = containerEl.querySelector("input[type='password']") as HTMLInputElement;
if (input && input.value.trim()) {
const key = input.value.trim();
if (key.startsWith("nsec1") || /^[0-9a-f]{64}$/i.test(key)) {
this.plugin.settings.privateKey = key;
await this.plugin.saveSettings();
input.value = "";
new Notice("Private key updated successfully");
await this.display();
} else {
new Notice("Invalid key format. Expected nsec1... or 64-char hex string.");
}
}
});
});
} catch (error: any) { } catch (error: any) {
new Setting(containerEl) new Setting(containerEl)
.setName("Private Key Status") .setName("Private Key Status")
@ -121,39 +95,17 @@ export class ScriptoriumSettingTab extends PluginSettingTab {
} else { } else {
new Setting(containerEl) new Setting(containerEl)
.setName("Private Key") .setName("Private Key")
.setDesc("Enter your private key manually, or set SCRIPTORIUM_OBSIDIAN_KEY environment variable, or create .scriptorium_key file in vault root.") .setDesc("Set SCRIPTORIUM_OBSIDIAN_KEY environment variable to load your private key. Use the startup script with --generate-key to create a new key.")
.addText((text) => {
text.setPlaceholder("nsec1... or 64-char hex key")
.inputEl.type = "password";
})
.addButton((button) => {
button.setButtonText("Set Key")
.setCta()
.onClick(async () => {
const input = containerEl.querySelector("input[type='password']") as HTMLInputElement;
if (input && input.value.trim()) {
const key = input.value.trim();
if (key.startsWith("nsec1") || /^[0-9a-f]{64}$/i.test(key)) {
this.plugin.settings.privateKey = key;
await this.plugin.saveSettings();
input.value = "";
new Notice("Private key saved successfully");
await this.display();
} else {
new Notice("Invalid key format. Expected nsec1... or 64-char hex string.");
}
}
});
})
.addButton((button) => { .addButton((button) => {
button.setButtonText("Refresh") button.setButtonText("Refresh")
.setCta()
.onClick(async () => { .onClick(async () => {
const loaded = await this.plugin.loadPrivateKey(); const loaded = await this.plugin.loadPrivateKey();
if (loaded) { if (loaded) {
new Notice("Private key loaded successfully"); new Notice("Private key loaded from environment variable");
await this.display(); await this.display();
} else { } else {
new Notice("Could not load private key. Please enter it manually above."); new Notice("Could not load private key. Set SCRIPTORIUM_OBSIDIAN_KEY environment variable and restart Obsidian.");
} }
}); });
}); });

556
start-obsidian.sh

@ -0,0 +1,556 @@
#!/bin/bash
# Script to build, install, and start Obsidian with the Scriptorium plugin
# Usage: ./start-obsidian.sh [path-to-obsidian-vault]
# On first run, path is required. Subsequent runs will use the saved path.
set -e # Exit on error
# Function to show help
show_help() {
cat << EOF
Scriptorium Obsidian Plugin - Startup Script
USAGE:
$0 [OPTIONS] [path-to-obsidian-vault]
OPTIONS:
-h, --help Show this help message and exit
--generate-key Generate a new Nostr private key
The key will be shown for you to save as an environment variable
(most secure option - hides key from GUI)
ARGUMENTS:
path-to-obsidian-vault
Path to your Obsidian vault (the folder containing .obsidian)
Required on first run. Optional on subsequent runs.
Note: The .obsidian folder may be hidden in your file manager.
EXAMPLES:
# First run (path required):
$0 ~/Documents/MyVault
# Subsequent runs (uses saved path):
$0
# Change to a different vault:
$0 ~/Documents/NewVault
# Generate a new Nostr private key:
$0 --generate-key [vault-path]
# Show help:
$0 --help
DESCRIPTION:
This script will:
1. Install npm dependencies (if needed)
2. Build the Scriptorium plugin
3. Install it to your Obsidian vault's plugin folder
4. Install and enable the obsidian-asciidoc plugin (required for .adoc files)
5. Start Obsidian with console logging enabled
Note: The obsidian-asciidoc plugin is required for this plugin to work properly
with .adoc files. It will be installed and enabled automatically.
On first run, you must provide the vault path. The path will be saved
to .scriptorium-vault-path for future use.
If the saved path becomes invalid (vault moved/deleted), you'll be
prompted to provide a new path.
The obsidian-asciidoc plugin is recommended for editing .adoc files
in Obsidian without crashes. It provides proper syntax highlighting
and editing support.
NOTES:
- The vault path is the folder containing the .obsidian directory
- The .obsidian folder may be hidden in your file manager (enable "Show hidden files")
- Plugin logs will appear in this terminal
- Press Ctrl+C to stop Obsidian
EOF
exit 0
}
# Check for help flag
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
show_help
fi
# Check for generate-key flag
GENERATE_KEY=false
if [ "$1" = "--generate-key" ]; then
GENERATE_KEY=true
# Remove the flag from arguments
shift
fi
# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VAULT_CONFIG_FILE="${SCRIPT_DIR}/.scriptorium-vault-path"
# Function to check if a vault path is valid
is_valid_vault_path() {
local path="$1"
# Empty path is invalid
if [ -z "$path" ]; then
return 1
fi
# Check if path exists as a directory, or if parent directory exists (so we can create it)
if [ -d "$path" ]; then
return 0
elif [ -d "$(dirname "$path" 2>/dev/null)" ]; then
# Parent exists, so we can create the vault directory
return 0
fi
return 1
}
# Function to load saved vault path
load_vault_path() {
if [ -f "$VAULT_CONFIG_FILE" ]; then
local saved_path=$(cat "$VAULT_CONFIG_FILE" | head -n 1 | tr -d '\n\r')
if [ -n "$saved_path" ] && is_valid_vault_path "$saved_path"; then
echo "$saved_path"
return 0
fi
fi
return 1
}
# Function to save vault path
save_vault_path() {
local path="$1"
echo "$path" > "$VAULT_CONFIG_FILE"
echo "[Scriptorium] Saved vault path to $VAULT_CONFIG_FILE"
}
# Function to generate a new Nostr private key
generate_nostr_key() {
echo "[Scriptorium] Generating new Nostr private key..."
# Check if Node.js is available
if ! command -v node &> /dev/null; then
echo "Error: Node.js is required to generate a Nostr key"
echo "Please install Node.js and try again"
exit 1
fi
# Generate key using Node.js and nostr-tools
# We'll use a temporary script to generate the key
local temp_script=$(mktemp)
cat > "$temp_script" << 'NODE_SCRIPT'
const { generatePrivateKey, getPublicKey } = require('nostr-tools');
const { nip19 } = require('nostr-tools');
try {
// Generate a new private key (32 bytes, hex encoded)
const privkey = generatePrivateKey();
// Get the public key to verify
const pubkey = getPublicKey(privkey);
// Encode as nsec1 (bech32)
const nsec = nip19.nsecEncode(privkey);
const npub = nip19.npubEncode(pubkey);
// Output both formats
console.log(JSON.stringify({
nsec: nsec,
hex: privkey,
npub: npub,
pubkey: pubkey
}));
} catch (error) {
console.error(JSON.stringify({ error: error.message }));
process.exit(1);
}
NODE_SCRIPT
# Run the script
local key_data=$(node "$temp_script" 2>/dev/null)
rm -f "$temp_script"
if [ -z "$key_data" ] || echo "$key_data" | grep -q '"error"'; then
echo "Error: Failed to generate Nostr key"
echo "Make sure nostr-tools is installed: npm install"
exit 1
fi
# Parse the JSON output
local nsec=$(echo "$key_data" | node -e "const d=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(d.nsec)")
local npub=$(echo "$key_data" | node -e "const d=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(d.npub)")
echo ""
echo "Generated new Nostr key:"
echo " Public key (npub): $npub"
echo ""
echo "To use this key, set it as an environment variable:"
echo ""
echo " export SCRIPTORIUM_OBSIDIAN_KEY=\"$nsec\""
echo ""
echo "Add this line to your shell profile (~/.bashrc, ~/.zshrc, etc.) to make it permanent:"
echo " export SCRIPTORIUM_OBSIDIAN_KEY=\"$nsec\""
echo ""
echo "Then launch Obsidian from that terminal (or restart your shell) to use the key, by typing 'obsidian' or './start-obsidian.sh'."
echo ""
echo "Note: The private key is hidden from the GUI for security. Only the public key (npub) is shown."
echo "To view the private key, you can use the following command:"
echo ""
echo " echo $nsec"
echo ""
echo "This will show the private key in clear text."
echo "Remember: The private key is only visible in the terminal and will not be available, unless you save it as an environment variable. Do not share it with anyone!"
}
# If generating key, handle it separately (no vault path needed)
if [ "$GENERATE_KEY" = true ]; then
cd "$SCRIPT_DIR"
generate_nostr_key
exit 0
fi
# Determine vault path
VAULT_PATH=""
# If path is provided as argument, use it
if [ -n "$1" ]; then
VAULT_PATH="$1"
# Expand ~ and resolve relative paths
VAULT_PATH=$(eval echo "$VAULT_PATH")
# Try to resolve to absolute path
if [ -d "$VAULT_PATH" ]; then
VAULT_PATH=$(cd "$VAULT_PATH" && pwd)
elif [ -d "$(dirname "$VAULT_PATH")" ]; then
# Parent exists, resolve parent and append basename
VAULT_PATH=$(cd "$(dirname "$VAULT_PATH")" && pwd)/$(basename "$VAULT_PATH")
else
# Can't resolve, use as-is
VAULT_PATH="$VAULT_PATH"
fi
# Validate provided path
if ! is_valid_vault_path "$VAULT_PATH"; then
echo "Error: The provided path does not appear to be valid: $VAULT_PATH"
echo "Please provide a valid path to your Obsidian vault"
echo "Note: The .obsidian folder may be hidden in your file manager."
exit 1
fi
# Save the path for next time
save_vault_path "$VAULT_PATH"
echo "[Scriptorium] Using vault path: $VAULT_PATH"
else
# Try to load saved path
# Use || true to prevent set -e from exiting if load_vault_path fails
SAVED_PATH=$(load_vault_path || true)
if [ -n "$SAVED_PATH" ]; then
# Re-validate the saved path
if is_valid_vault_path "$SAVED_PATH"; then
VAULT_PATH="$SAVED_PATH"
echo "[Scriptorium] Using saved vault path: $VAULT_PATH"
else
echo "Error: Saved vault path is no longer valid: $SAVED_PATH"
echo ""
echo "The vault may have been moved or deleted. Please provide a new vault path:"
echo ""
echo "Usage: $0 <path-to-obsidian-vault>"
echo "Example: $0 ~/Documents/MyVault"
echo ""
echo "Note: The .obsidian folder may be hidden in your file manager."
echo "For more information, run: $0 --help"
exit 1
fi
else
echo "Error: No vault path provided and no saved path found"
echo ""
echo "This appears to be your first run. Please provide the path to your Obsidian vault:"
echo ""
echo "Usage: $0 <path-to-obsidian-vault>"
echo "Example: $0 ~/Documents/MyVault"
echo ""
echo "The vault path is the folder containing the .obsidian directory."
echo "Note: The .obsidian folder may be hidden in your file manager (enable 'Show hidden files')."
echo "After the first run, the path will be saved and you won't need to provide it again."
echo ""
echo "For more information, run: $0 --help"
exit 1
fi
fi
# Final validation
if [ -z "$VAULT_PATH" ]; then
echo "Error: Could not determine vault path"
exit 1
fi
OBSIDIAN_FOLDER="${VAULT_PATH}/.obsidian"
PLUGIN_FOLDER="${OBSIDIAN_FOLDER}/plugins/scriptorium-obsidian"
# Change to script directory
cd "$SCRIPT_DIR"
echo "[Scriptorium] Starting build and install process..."
echo "[Scriptorium] Vault path: $VAULT_PATH"
echo "[Scriptorium] Plugin folder: $PLUGIN_FOLDER"
# Check if npm dependencies need to be installed
if [ ! -d "node_modules" ] || [ "package.json" -nt "node_modules" ]; then
echo "[Scriptorium] Installing npm dependencies..."
npm install
if [ $? -ne 0 ]; then
echo "Error: npm install failed"
exit 1
fi
echo "[Scriptorium] Dependencies installed successfully"
else
echo "[Scriptorium] Dependencies are up to date"
fi
# Create .obsidian folder if it doesn't exist
if [ ! -d "$OBSIDIAN_FOLDER" ]; then
echo "[Scriptorium] Creating .obsidian folder..."
mkdir -p "$OBSIDIAN_FOLDER"
fi
# Create plugins folder if it doesn't exist
if [ ! -d "${OBSIDIAN_FOLDER}/plugins" ]; then
echo "[Scriptorium] Creating plugins folder..."
mkdir -p "${OBSIDIAN_FOLDER}/plugins"
fi
# Create plugin directory if it doesn't exist
if [ ! -d "$PLUGIN_FOLDER" ]; then
echo "[Scriptorium] Creating plugin directory..."
mkdir -p "$PLUGIN_FOLDER"
fi
# Build the plugin
echo "[Scriptorium] Building plugin..."
npm run build
# Check if build was successful
if [ ! -f "main.js" ] || [ ! -f "manifest.json" ]; then
echo "Error: Build failed - main.js or manifest.json not found"
exit 1
fi
# Function to check if file needs to be copied
needs_copy() {
local source_file="$1"
local dest_file="$2"
# If destination doesn't exist, we need to copy
if [ ! -f "$dest_file" ]; then
return 0 # true - needs copy
fi
# If source is newer than destination, we need to copy
if [ "$source_file" -nt "$dest_file" ]; then
return 0 # true - needs copy
fi
return 1 # false - no copy needed
}
# Copy main.js if needed
if needs_copy "main.js" "${PLUGIN_FOLDER}/main.js"; then
echo "[Scriptorium] Copying main.js to plugin folder..."
cp "main.js" "${PLUGIN_FOLDER}/main.js"
else
echo "[Scriptorium] main.js is up to date, skipping copy"
fi
# Copy manifest.json if needed
if needs_copy "manifest.json" "${PLUGIN_FOLDER}/manifest.json"; then
echo "[Scriptorium] Copying manifest.json to plugin folder..."
cp "manifest.json" "${PLUGIN_FOLDER}/manifest.json"
else
echo "[Scriptorium] manifest.json is up to date, skipping copy"
fi
echo "[Scriptorium] Plugin installed successfully!"
# Function to enable a plugin in Obsidian
enable_plugin() {
local plugin_id="$1"
local plugins_json="${OBSIDIAN_FOLDER}/community-plugins.json"
# Create plugins JSON file if it doesn't exist
if [ ! -f "$plugins_json" ]; then
echo "[]" > "$plugins_json"
fi
# Check if plugin is already enabled
if grep -q "\"$plugin_id\"" "$plugins_json" 2>/dev/null; then
echo "[Scriptorium] Plugin $plugin_id is already enabled"
return 0
fi
# Add plugin to enabled list using Node.js or Python
if command -v node &> /dev/null; then
# Use Node.js to update JSON
node << NODE_SCRIPT
const fs = require('fs');
const pluginsJson = '${plugins_json}';
let plugins = [];
try {
const content = fs.readFileSync(pluginsJson, 'utf8');
plugins = JSON.parse(content);
} catch (e) {
plugins = [];
}
if (!Array.isArray(plugins)) {
plugins = [];
}
if (!plugins.includes('${plugin_id}')) {
plugins.push('${plugin_id}');
fs.writeFileSync(pluginsJson, JSON.stringify(plugins, null, 2));
console.log('Enabled plugin: ${plugin_id}');
}
NODE_SCRIPT
if [ $? -eq 0 ]; then
echo "[Scriptorium] Enabled plugin: $plugin_id"
else
echo "[Scriptorium] Warning: Could not enable plugin $plugin_id automatically"
fi
elif command -v python3 &> /dev/null; then
# Fallback to Python
python3 << PYTHON_SCRIPT
import json
import os
plugins_json = '${plugins_json}'
plugin_id = '${plugin_id}'
try:
with open(plugins_json, 'r') as f:
plugins = json.load(f)
except:
plugins = []
if not isinstance(plugins, list):
plugins = []
if plugin_id not in plugins:
plugins.append(plugin_id)
with open(plugins_json, 'w') as f:
json.dump(plugins, f, indent=2)
print(f'Enabled plugin: {plugin_id}')
PYTHON_SCRIPT
if [ $? -eq 0 ]; then
echo "[Scriptorium] Enabled plugin: $plugin_id"
else
echo "[Scriptorium] Warning: Could not enable plugin $plugin_id automatically"
fi
else
echo "[Scriptorium] Warning: Node.js or Python3 required to auto-enable plugins"
echo "[Scriptorium] Please enable $plugin_id manually in Obsidian settings"
fi
}
# Enable scriptorium-obsidian plugin
echo "[Scriptorium] Enabling scriptorium-obsidian plugin..."
enable_plugin "scriptorium-obsidian"
# Install obsidian-asciidoc plugin (required for .adoc file support)
ASCIIDOC_PLUGIN_DIR="${OBSIDIAN_FOLDER}/plugins/obsidian-asciidoc"
ASCIIDOC_PLUGIN_REPO="https://github.com/dzruyk/obsidian-asciidoc.git"
echo "[Scriptorium] Installing obsidian-asciidoc plugin (required for .adoc files)..."
if [ -d "$ASCIIDOC_PLUGIN_DIR" ]; then
echo "[Scriptorium] obsidian-asciidoc plugin already exists, updating..."
cd "$ASCIIDOC_PLUGIN_DIR"
if [ -d ".git" ]; then
git pull || echo "[Scriptorium] Warning: Could not update plugin (git pull failed)"
# Rebuild after update
if [ -f "package.json" ]; then
echo "[Scriptorium] Rebuilding obsidian-asciidoc plugin..."
npm install && npm run build || {
echo "[Scriptorium] Warning: Could not rebuild obsidian-asciidoc plugin"
}
fi
else
echo "[Scriptorium] Warning: Plugin directory exists but is not a git repository"
fi
cd "$SCRIPT_DIR"
# Ensure plugin is enabled
ASCIIDOC_PLUGIN_ID=$(node -e "const m=require('${ASCIIDOC_PLUGIN_DIR}/manifest.json'); console.log(m.id)" 2>/dev/null || echo "obsidian-asciidoc")
enable_plugin "$ASCIIDOC_PLUGIN_ID"
else
echo "[Scriptorium] Cloning obsidian-asciidoc plugin..."
if command -v git &> /dev/null; then
git clone "$ASCIIDOC_PLUGIN_REPO" "$ASCIIDOC_PLUGIN_DIR" || {
echo "[Scriptorium] Error: Could not clone obsidian-asciidoc plugin"
echo "[Scriptorium] Make sure git is installed and you have internet access"
exit 1
}
if [ -d "$ASCIIDOC_PLUGIN_DIR" ]; then
echo "[Scriptorium] Building obsidian-asciidoc plugin..."
cd "$ASCIIDOC_PLUGIN_DIR"
if [ -f "package.json" ]; then
npm install && npm run build || {
echo "[Scriptorium] Error: Could not build obsidian-asciidoc plugin"
echo "[Scriptorium] Check the error messages above"
exit 1
}
else
echo "[Scriptorium] Warning: No package.json found in obsidian-asciidoc plugin"
fi
cd "$SCRIPT_DIR"
# Verify installation
if [ -f "${ASCIIDOC_PLUGIN_DIR}/main.js" ] && [ -f "${ASCIIDOC_PLUGIN_DIR}/manifest.json" ]; then
echo "[Scriptorium] obsidian-asciidoc plugin installed successfully!"
# Get plugin ID from manifest.json
ASCIIDOC_PLUGIN_ID=$(node -e "const m=require('${ASCIIDOC_PLUGIN_DIR}/manifest.json'); console.log(m.id)" 2>/dev/null || echo "obsidian-asciidoc")
# Enable the plugin
echo "[Scriptorium] Enabling obsidian-asciidoc plugin..."
enable_plugin "$ASCIIDOC_PLUGIN_ID"
else
echo "[Scriptorium] Error: Plugin files not found after installation"
echo "[Scriptorium] main.js or manifest.json missing in ${ASCIIDOC_PLUGIN_DIR}"
exit 1
fi
fi
else
echo "[Scriptorium] Error: git is not installed. Cannot install obsidian-asciidoc plugin"
echo "[Scriptorium] Please install git or install the plugin manually"
exit 1
fi
fi
# Start Obsidian
echo "[Scriptorium] Starting Obsidian..."
echo "[Scriptorium] Note: Plugin logs will appear in this terminal"
# Try to find Obsidian command
if command -v obsidian &> /dev/null; then
# Start Obsidian with the vault path
obsidian "$VAULT_PATH" &
echo "[Scriptorium] Obsidian started in background (PID: $!)"
echo "[Scriptorium] You can close this terminal, but plugin logs will stop if you do"
echo "[Scriptorium] Press Ctrl+C to stop Obsidian (or close the window)"
echo ""
echo "[Scriptorium] To use a different vault next time, run: $0 <new-vault-path>"
# Wait for user interrupt
wait
elif command -v flatpak &> /dev/null && flatpak list | grep -q "md.obsidian"; then
# Try Flatpak version
echo "[Scriptorium] Starting Obsidian via Flatpak..."
flatpak run md.obsidian.Obsidian "$VAULT_PATH" &
echo "[Scriptorium] Obsidian started in background (PID: $!)"
echo "[Scriptorium] You can close this terminal, but plugin logs will stop if you do"
echo "[Scriptorium] Press Ctrl+C to stop Obsidian (or close the window)"
echo ""
echo "[Scriptorium] To use a different vault next time, run: $0 <new-vault-path>"
# Wait for user interrupt
wait
else
echo "Error: Could not find Obsidian command"
echo "Please install Obsidian or add it to your PATH"
echo "Or start Obsidian manually and open vault: $VAULT_PATH"
exit 1
fi
Loading…
Cancel
Save