Command to create tracker

added a Initialize MultiMuse Workspace command to the plugin to allow easy setup of the tracker needed for the plugin
This commit is contained in:
Tea 2026-05-16 12:52:16 -04:00
parent 9dc2fca743
commit 430af632d5
5 changed files with 200 additions and 13 deletions

View file

@ -16,7 +16,10 @@ An Obsidian plugin for seamless integration with the MultiMuse Discord bot. Trac
## Installation
### Manual Installation
Install via the Obsidian plugin library
OR
Install via [BRAT](https://obsidian.md/plugins?id=obsidian42-brat) with https://github.com/BackstagePassGroup/multimuse-obsidian
@ -33,13 +36,27 @@ Install via [BRAT](https://obsidian.md/plugins?id=obsidian42-brat) with https://
1. Open Obsidian Settings → Multimuse Tracker
2. Paste your API key in the "API Key" field
3. Your Discord user ID will be automatically detected from the API key
4. Configure other settings:
- **Enable Polling**: Turn automatic checking on/off
- **Poll Interval**: How often to check for new replies (5-60 minutes)
- **Scenes Folder**: Folder containing your scene files (default: "RP Scenes")
- **Obsidian Base Path**: Optional path to Base file for scene tracking
- **Track Roleplay Property**: Automatically extract "Roleplay" from folder path
- **Track Is Active? Property**: Automatically add "Is Active?" property (defaults to true)
4. Adjust paths and property toggles if you want something other than the defaults:
- **Scenes Folder**: Where scene notes live (default: `RP Scenes`)
- **Track Roleplay Property** / **Track Is Active? Property**: Match how you want new scenes to get frontmatter (defaults on)
- **Obsidian Base Path**: Optional. Leave empty for first-time setup (see below), or set to a `.base` or `.md` file you already use
- **Enable Polling** / **Poll Interval**: How often threads are checked
### 3. First-time vault layout (recommended)
Use a single command to create your scenes folder and a Base aligned with your settings:
1. Open the Command Palette and run **Initialize MultiMuse workspace**
2. The plugin will:
- Create your **Scenes Folder** in the vault if it does not exist yet
- If **Obsidian Base Path** is empty, create `<Scenes Folder>/MultiMuse Scenes.base` (native Obsidian Base) and save that path in settings
- If **Obsidian Base Path** is set to a path ending in `.base` or `.md` that does not exist yet, create that file instead and keep the path as configured
**Native `.base` file:** The generated Base lists markdown files under your scenes folder, filters to **Is Active?** = true when that property toggle is on, and shows columns for `Link`, `Characters`, optional `Roleplay` / `Is Active?`, `Participants`, `Replied?`, and `Created`. Requires an Obsidian version that supports **Bases**.
**Markdown `.md` tracker:** If your Base path ends in `.md`, the command creates a starter pipe table. The plugin can append rows to that table when you create or sync scenes (it does not edit `.base` files programmatically; use the Base UI for those).
You can run **Initialize MultiMuse workspace** again only after removing or renaming the target file, or after changing **Obsidian Base Path**.
## Usage
@ -100,6 +117,7 @@ Created: 2024-01-15
## Commands
- **Initialize MultiMuse workspace**: Create your **Scenes Folder** and a Base (`.base`) or markdown tracker (`.md`) from settings; sets **Obsidian Base Path** when you start with it empty
- **Check Discord Threads Now**: Manually trigger a check for all scenes
- **Toggle Discord Polling**: Enable/disable automatic polling
- **Create New Scene**: Create a new scene file with muse selection

171
main.ts
View file

@ -183,6 +183,15 @@ export default class MultimuseObsidian extends Plugin {
}
});
// First-time vault layout: scenes folder + Base (.base or .md) from settings
this.addCommand({
id: 'initialize-multimuse-workspace',
name: 'Initialize MultiMuse workspace',
callback: () => {
void this.initializeMultimuseWorkspace();
}
});
// Add command to insert Discord @ mention (guild members from Link property)
this.addCommand({
id: 'insert-mention',
@ -1479,6 +1488,166 @@ export default class MultimuseObsidian extends Plugin {
// ========= BASE INTEGRATION =========
/** Ensure each segment of `folderPath` exists under the vault root. */
async ensureFolderPathExists(folderPath: string): Promise<void> {
const normalized = folderPath.replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
if (!normalized) return;
const parts = normalized.split('/').filter((p) => p.length > 0);
let acc = '';
for (const part of parts) {
acc = acc ? `${acc}/${part}` : part;
const existing = this.app.vault.getAbstractFileByPath(acc);
if (!existing) {
await this.app.vault.createFolder(acc);
}
}
}
/**
* YAML for a native Obsidian Base listing scene notes under the scenes folder,
* with columns aligned to MultiMuse frontmatter and plugin toggles.
*/
buildSceneBaseYaml(scenesFolder: string): string {
const folderLit = JSON.stringify(scenesFolder);
const lines: string[] = [
'# MultiMuse Tracker — generated Base (safe to edit in Obsidian)',
'filters:',
' and:',
' - file.ext == "md"',
` - file.inFolder(${folderLit})`,
];
if (this.settings.trackIsActive) {
lines.push(' - \'note["Is Active?"] == true\'');
}
lines.push(
'properties:',
' file.name:',
' displayName: Scene',
' file.path:',
' displayName: Path',
' Link:',
' displayName: Link',
' Characters:',
' displayName: Characters',
);
if (this.settings.trackRoleplay) {
lines.push(' Roleplay:', ' displayName: Roleplay');
}
lines.push(
' Participants:',
' displayName: Participants',
" 'Replied?':",
' displayName: Replied?',
);
if (this.settings.trackIsActive) {
lines.push(" 'Is Active?':", ' displayName: Is Active?');
}
lines.push(
' Created:',
' displayName: Created',
'views:',
' - type: table',
' name: Roleplay Tracker',
' order:',
' - file.name',
' - Link',
' - Characters',
);
if (this.settings.trackRoleplay) {
lines.push(' - Roleplay');
}
lines.push(
' - Participants',
" - 'Replied?'",
);
if (this.settings.trackIsActive) {
lines.push(" - 'Is Active?'");
}
lines.push(' - Created');
return lines.join('\n') + '\n';
}
/** Starter markdown table compatible with this plugin's markdown Base integration. */
buildMarkdownTrackerStub(): string {
return [
'# MultiMuse scene tracker',
'',
'Rows below are appended when you create or sync scenes if **Obsidian Base Path** points to this file.',
'',
'| Scene | Characters | Link | Participants | Replied? |',
'|-------|------------|------|--------------|----------|',
'',
].join('\n');
}
/**
* Create the configured scenes folder (if missing), then a new Base or markdown tracker file.
* If **Obsidian Base Path** is empty, creates `<Scenes Folder>/Roleplay Tracker.base` and saves that path.
*/
async initializeMultimuseWorkspace(): Promise<void> {
const scenesFolder = this.settings.scenesFolder
.trim()
.replace(/\\/g, '/')
.replace(/^\/+|\/+$/g, '');
if (!scenesFolder) {
new Notice('Set Scenes Folder in Multimuse Tracker settings first.');
return;
}
const configuredBase = this.settings.basePath
.trim()
.replace(/\\/g, '/')
.replace(/^\/+|\/+$/g, '');
let targetBasePath: string;
if (!configuredBase) {
targetBasePath = `${scenesFolder}/Roleplay Tracker.base`;
} else {
targetBasePath = configuredBase;
}
const ext = (targetBasePath.split('.').pop() || '').toLowerCase();
if (ext !== 'base' && ext !== 'md') {
new Notice('Obsidian Base Path must end in .base or .md, or leave it empty to create Roleplay Tracker.base under your scenes folder.');
return;
}
try {
await this.ensureFolderPathExists(scenesFolder);
const existing = this.app.vault.getAbstractFileByPath(targetBasePath);
if (existing) {
new Notice(`Already exists: ${targetBasePath}. Remove it or change Obsidian Base Path in settings, then run again.`);
return;
}
const parent = targetBasePath.includes('/')
? targetBasePath.slice(0, targetBasePath.lastIndexOf('/'))
: '';
if (parent) {
await this.ensureFolderPathExists(parent);
}
const body = ext === 'base'
? this.buildSceneBaseYaml(scenesFolder)
: this.buildMarkdownTrackerStub();
await this.app.vault.create(targetBasePath, body);
this.settings.basePath = targetBasePath;
await this.saveSettings();
new Notice(
ext === 'base'
? `Created Base and set path: ${targetBasePath}`
: `Created markdown tracker and set path: ${targetBasePath}`,
);
} catch (error) {
console.error('[MultimuseObsidian] initializeMultimuseWorkspace:', error);
new Notice(`Could not initialize workspace: ${getErrorMessage(error)}`);
}
}
async addSceneToBase(file: TFile, frontmatter: FrontmatterData): Promise<void> {
/**Add scene to Obsidian Base with characters as variables.*/
if (!this.settings.basePath) return;
@ -2061,7 +2230,7 @@ class MultimuseObsidianSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName('Obsidian Base Path')
.setDesc('Path to your Obsidian Base file (e.g., "RP Scenes/Roleplay Tracker.base" or "RP Scenes/Tracker.md")')
.setDesc('Path to your Obsidian Base file (e.g., "RP Scenes/Roleplay Tracker.base" or "RP Scenes/Tracker.md"). Leave empty and run **Initialize MultiMuse workspace** to create `<Scenes Folder>/Roleplay Tracker.base`.')
.addText(text => text
.setPlaceholder('RP Scenes/Roleplay Tracker.base')
.setValue(this.plugin.settings.basePath)

View file

@ -1,7 +1,7 @@
{
"id": "multimuse-tracker",
"name": "Multimuse Tracker",
"version": "1.10.1",
"version": "1.11.0",
"minAppVersion": "1.4.0",
"description": "Roleplay sync tool for use with the MultiMuse bot in discord.",
"author": "BackstagePass Group",

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "multimuse-tracker",
"version": "1.10.1",
"version": "1.11.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "multimuse-tracker",
"version": "1.10.1",
"version": "1.11.0",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",

View file

@ -1,6 +1,6 @@
{
"name": "multimuse-tracker",
"version": "1.10.1",
"version": "1.11.0",
"description": "MultiMuse bot integration for tracking Discord threads and sending messages as muses",
"main": "main.js",
"scripts": {