mirror of
https://github.com/tea0s/multimuse-tracker.git
synced 2026-07-22 06:56:41 +00:00
1.10.0 Update
This commit is contained in:
parent
4879d84188
commit
e51eb40aaa
7 changed files with 501 additions and 348 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# Multimuse Obsidian
|
||||
# Multimuse Tracker
|
||||
For use with the BRAT Plugin for the best experiance.
|
||||
|
||||
An Obsidian plugin for seamless integration with the MultiMuse Discord bot. Track Discord roleplay threads, send messages as muses directly from Obsidian, and automatically sync scene states.
|
||||
|
|
@ -30,7 +30,7 @@ Install via [BRAT](https://obsidian.md/plugins?id=obsidian42-brat) with https://
|
|||
|
||||
### 2. Configure Plugin
|
||||
|
||||
1. Open Obsidian Settings → Multimuse Obsidian
|
||||
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:
|
||||
|
|
@ -209,4 +209,4 @@ MIT
|
|||
## Credits
|
||||
|
||||
- **Author**: BackstagePass Group
|
||||
- **Plugin**: Multimuse Obsidian
|
||||
- **Plugin**: Multimuse Tracker
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import esbuild from "esbuild";
|
||||
import process from "process";
|
||||
import builtins from "builtin-modules";
|
||||
import { builtinModules } from "module";
|
||||
|
||||
const isProduction = process.argv.includes("production");
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ const ctx = await esbuild.context({
|
|||
"@lezer/common",
|
||||
"@lezer/highlight",
|
||||
"@lezer/lr",
|
||||
...builtins],
|
||||
...builtinModules],
|
||||
format: "cjs",
|
||||
target: "es2018",
|
||||
logLevel: "info",
|
||||
|
|
|
|||
438
main.ts
438
main.ts
|
|
@ -1,4 +1,4 @@
|
|||
import { Plugin, PluginSettingTab, Setting, Notice, TFile, MetadataCache, App, requestUrl, Modal, Editor, MarkdownView } from 'obsidian';
|
||||
import { Plugin, PluginSettingTab, Setting, Notice, TFile, TFolder, TAbstractFile, App, requestUrl, Modal, Editor, MarkdownView, CachedMetadata, RequestUrlResponse } from 'obsidian';
|
||||
|
||||
interface MultimuseObsidianSettings {
|
||||
botApiUrl: string; // Bot HTTP API URL (hidden from user UI for security)
|
||||
|
|
@ -40,6 +40,80 @@ interface MuseInfo {
|
|||
muse_id?: string | null; // Optional: for API calls (alias-safe); display always uses name
|
||||
}
|
||||
|
||||
type FrontmatterData = Record<string, unknown>;
|
||||
type FrontmatterValue = string | number | boolean | string[];
|
||||
|
||||
interface AuthMeResponse {
|
||||
user_id?: string | number;
|
||||
}
|
||||
|
||||
interface MusesListResponse {
|
||||
muses?: MuseInfo[];
|
||||
}
|
||||
|
||||
interface SceneState {
|
||||
replied?: boolean | string | null;
|
||||
is_from_character?: boolean | string | null;
|
||||
timestamp?: string | null;
|
||||
your_last_post?: string | null;
|
||||
posted_since_count?: number | null;
|
||||
}
|
||||
|
||||
interface SceneQueryResponse {
|
||||
tracked?: boolean;
|
||||
state?: SceneState | null;
|
||||
}
|
||||
|
||||
interface TrackedThread {
|
||||
thread_id: string | number;
|
||||
muse_name?: string;
|
||||
muse_names?: string[];
|
||||
participants?: number | string;
|
||||
scene_path?: string;
|
||||
scene_paths?: string[];
|
||||
guild_id?: string | number | null;
|
||||
thread_name?: string;
|
||||
}
|
||||
|
||||
interface TrackedThreadsResponse {
|
||||
threads?: TrackedThread[];
|
||||
}
|
||||
|
||||
interface GuildMember {
|
||||
id: string;
|
||||
username: string;
|
||||
display_name: string;
|
||||
}
|
||||
|
||||
interface GuildMembersResponse {
|
||||
members?: GuildMember[];
|
||||
}
|
||||
|
||||
interface ApiErrorBody {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
function parseJson<T>(text: string): T {
|
||||
return JSON.parse(text) as T;
|
||||
}
|
||||
|
||||
function getErrorMessage(error: unknown): string {
|
||||
if (error && typeof error === 'object') {
|
||||
const details = error as { message?: unknown; text?: unknown };
|
||||
if (typeof details.message === 'string') return details.message;
|
||||
if (typeof details.text === 'string') return details.text;
|
||||
}
|
||||
return 'Unknown error';
|
||||
}
|
||||
|
||||
function getErrorStatus(error: unknown): number | undefined {
|
||||
if (error && typeof error === 'object') {
|
||||
const details = error as { status?: unknown };
|
||||
if (typeof details.status === 'number') return details.status;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export default class MultimuseObsidian extends Plugin {
|
||||
settings: MultimuseObsidianSettings;
|
||||
pollIntervalId: number | null = null;
|
||||
|
|
@ -71,7 +145,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
id: 'check-discord-threads',
|
||||
name: 'Check Discord Threads Now',
|
||||
callback: () => {
|
||||
this.checkAllThreads();
|
||||
void this.checkAllThreads();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -81,7 +155,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
name: 'Toggle Discord Polling',
|
||||
callback: () => {
|
||||
this.settings.enabled = !this.settings.enabled;
|
||||
this.saveSettings();
|
||||
void this.saveSettings();
|
||||
if (this.settings.enabled) {
|
||||
this.startPolling();
|
||||
new Notice('Discord polling enabled');
|
||||
|
|
@ -97,7 +171,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
id: 'create-scene',
|
||||
name: 'Create New Scene',
|
||||
callback: () => {
|
||||
this.createNewScene();
|
||||
void this.createNewScene();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -106,7 +180,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
id: 'sync-from-tracker',
|
||||
name: 'Sync from Tracker',
|
||||
callback: () => {
|
||||
this.syncFromTracker();
|
||||
void this.syncFromTracker();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -208,11 +282,11 @@ export default class MultimuseObsidian extends Plugin {
|
|||
|
||||
const intervalMs = this.settings.pollInterval * 60 * 1000;
|
||||
this.pollIntervalId = window.setInterval(() => {
|
||||
this.checkAllThreads();
|
||||
void this.checkAllThreads();
|
||||
}, intervalMs);
|
||||
|
||||
// Do an initial check
|
||||
this.checkAllThreads();
|
||||
void this.checkAllThreads();
|
||||
}
|
||||
|
||||
stopPolling() {
|
||||
|
|
@ -250,13 +324,47 @@ export default class MultimuseObsidian extends Plugin {
|
|||
return headers;
|
||||
}
|
||||
|
||||
async trackThread(params: {
|
||||
threadId: string;
|
||||
userId: string;
|
||||
museName: string;
|
||||
participants: number;
|
||||
scenePath?: string;
|
||||
guildId?: string | null;
|
||||
characters?: string[];
|
||||
}): Promise<RequestUrlResponse> {
|
||||
const body: Record<string, unknown> = {
|
||||
thread_id: params.threadId,
|
||||
user_id: params.userId,
|
||||
muse_name: params.museName,
|
||||
participants: params.participants
|
||||
};
|
||||
if (params.scenePath) body.scene_path = params.scenePath;
|
||||
if (params.guildId) body.guild_id = params.guildId;
|
||||
if (params.characters && params.characters.length > 0) body.characters = params.characters;
|
||||
|
||||
return await requestUrl({
|
||||
url: `${this.getBotApiUrl()}/api/v1/threads/track`,
|
||||
method: 'POST',
|
||||
headers: this.getApiHeaders(),
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
}
|
||||
|
||||
getFrontmatter(cache: CachedMetadata | null): FrontmatterData | null {
|
||||
if (!cache?.frontmatter) {
|
||||
return null;
|
||||
}
|
||||
return cache.frontmatter as FrontmatterData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle API response errors, especially authentication errors.
|
||||
* @param response The response object from requestUrl
|
||||
* @param context Context string for logging
|
||||
* @returns true if error was handled, false otherwise
|
||||
*/
|
||||
handleApiError(response: any, context: string): boolean {
|
||||
handleApiError(response: RequestUrlResponse, context: string): boolean {
|
||||
if (response.status === 401) {
|
||||
const errorMsg = 'API authentication failed. Please check your API key in settings.';
|
||||
console.error(`[MultimuseObsidian] ${context}: ${errorMsg}`);
|
||||
|
|
@ -291,7 +399,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.text);
|
||||
const data = parseJson<AuthMeResponse>(response.text);
|
||||
const userId = data.user_id;
|
||||
if (userId) {
|
||||
// Cache the user ID
|
||||
|
|
@ -369,7 +477,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.text);
|
||||
const data = parseJson<MusesListResponse>(response.text);
|
||||
const muses: MuseInfo[] = data.muses || [];
|
||||
|
||||
// Cache all muses for each user ID (API already returns all accessible muses for all provided user IDs)
|
||||
|
|
@ -400,7 +508,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
|
||||
async checkAllThreadsViaBotApi(): Promise<void> {
|
||||
/**Check all scene files by querying the tracker API for linked scenes in batch.*/
|
||||
/**Check all scene files against current thread_tracking state.*/
|
||||
if (!this.settings.apiKey) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -416,34 +524,37 @@ export default class MultimuseObsidian extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
// Get all linked scenes from the API
|
||||
const linkedUrl = `${this.getBotApiUrl()}/api/v1/scenes/linked?user_id=${primaryUserId}`;
|
||||
const linkedResponse = await requestUrl({
|
||||
url: linkedUrl,
|
||||
// Get all Discord-side tracked threads from the current API.
|
||||
const trackedUrl = `${this.getBotApiUrl()}/api/v1/threads/tracked?user_id=${primaryUserId}`;
|
||||
const trackedResponse = await requestUrl({
|
||||
url: trackedUrl,
|
||||
method: 'GET',
|
||||
headers: this.getApiHeaders()
|
||||
});
|
||||
|
||||
if (linkedResponse.status !== 200) {
|
||||
if (!this.handleApiError(linkedResponse, 'checkAllThreadsViaBotApi')) {
|
||||
console.error(`[MultimuseObsidian] Failed to fetch linked scenes: ${linkedResponse.status} - ${linkedResponse.text}`);
|
||||
if (trackedResponse.status !== 200) {
|
||||
if (!this.handleApiError(trackedResponse, 'checkAllThreadsViaBotApi')) {
|
||||
console.error(`[MultimuseObsidian] Failed to fetch tracked threads: ${trackedResponse.status} - ${trackedResponse.text}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const linkedData = JSON.parse(linkedResponse.text);
|
||||
const linkedThreads = linkedData.linked_threads || [];
|
||||
const trackedData = parseJson<TrackedThreadsResponse>(trackedResponse.text);
|
||||
const trackedThreads = trackedData.threads || [];
|
||||
|
||||
if (linkedThreads.length === 0) {
|
||||
if (trackedThreads.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a map of scene_path -> thread info for quick lookup
|
||||
const scenePathMap = new Map<string, any>();
|
||||
for (const thread of linkedThreads) {
|
||||
// Create a map of scene_path -> thread info for quick lookup.
|
||||
const scenePathMap = new Map<string, TrackedThread>();
|
||||
for (const thread of trackedThreads) {
|
||||
if (thread.scene_path) {
|
||||
scenePathMap.set(thread.scene_path, thread);
|
||||
}
|
||||
for (const scenePath of thread.scene_paths || []) {
|
||||
scenePathMap.set(scenePath, thread);
|
||||
}
|
||||
}
|
||||
|
||||
// Get all scene files
|
||||
|
|
@ -460,12 +571,13 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
|
||||
const cache = this.app.metadataCache.getFileCache(file);
|
||||
if (!cache || !cache.frontmatter) {
|
||||
const frontmatter = this.getFrontmatter(cache);
|
||||
if (!frontmatter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if scene is marked as inactive
|
||||
const isActive = cache.frontmatter['Is Active?'];
|
||||
const isActive = frontmatter['Is Active?'];
|
||||
if (isActive === false || isActive === 'false') {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -473,11 +585,11 @@ export default class MultimuseObsidian extends Plugin {
|
|||
// Check if this scene is in the linked threads
|
||||
const threadInfo = scenePathMap.get(file.path);
|
||||
if (!threadInfo) {
|
||||
// Scene not linked, try querying by thread_id and characters
|
||||
const link = cache.frontmatter['Link'];
|
||||
if (!link) continue;
|
||||
// Scene not linked in thread_tracking, try querying by thread_id and characters.
|
||||
const link = frontmatter['Link'];
|
||||
if (typeof link !== 'string') continue;
|
||||
|
||||
const characters = this.getCharacterNames(cache.frontmatter);
|
||||
const characters = this.getCharacterNames(frontmatter);
|
||||
if (characters.length === 0) continue;
|
||||
|
||||
const threadId = this.extractThreadIdFromUrl(link);
|
||||
|
|
@ -490,10 +602,10 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
} else {
|
||||
// Scene is linked - query its state
|
||||
const characters = threadInfo.characters || [];
|
||||
const characters = this.getCharacterNames(frontmatter);
|
||||
if (characters.length === 0) continue;
|
||||
|
||||
const charactersParam = Array.isArray(characters) ? characters.join(',') : characters;
|
||||
const charactersParam = characters.join(',');
|
||||
// Use primary user ID for query
|
||||
const primaryUserId = await this.getPrimaryUserId();
|
||||
if (!primaryUserId) {
|
||||
|
|
@ -508,7 +620,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
});
|
||||
|
||||
if (queryResponse.status === 200) {
|
||||
const queryData = JSON.parse(queryResponse.text);
|
||||
const queryData = parseJson<SceneQueryResponse>(queryResponse.text);
|
||||
|
||||
if (queryData.tracked && queryData.state) {
|
||||
const state = queryData.state;
|
||||
|
|
@ -546,22 +658,23 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
|
||||
const cache = this.app.metadataCache.getFileCache(file);
|
||||
if (!cache || !cache.frontmatter) {
|
||||
const frontmatter = this.getFrontmatter(cache);
|
||||
if (!cache || !frontmatter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip if scene is marked as inactive
|
||||
const isActive = cache.frontmatter['Is Active?'];
|
||||
const isActive = frontmatter['Is Active?'];
|
||||
if (isActive === false || isActive === 'false') {
|
||||
return false; // Skip inactive scenes
|
||||
}
|
||||
|
||||
const link = cache.frontmatter['Link'];
|
||||
if (!link) {
|
||||
const link = frontmatter['Link'];
|
||||
if (typeof link !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const characters = this.getCharacterNames(cache.frontmatter);
|
||||
const characters = this.getCharacterNames(frontmatter);
|
||||
if (characters.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -594,7 +707,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
return false;
|
||||
}
|
||||
|
||||
const data = JSON.parse(response.text);
|
||||
const data = parseJson<SceneQueryResponse>(response.text);
|
||||
|
||||
// If scene is not tracked, don't update anything
|
||||
if (!data.tracked || !data.state) {
|
||||
|
|
@ -621,9 +734,13 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
async updateSceneFromState(file: TFile, cache: any, state: any): Promise<boolean> {
|
||||
async updateSceneFromState(file: TFile, cache: CachedMetadata, state: SceneState): Promise<boolean> {
|
||||
/**Update scene frontmatter from API state data.*/
|
||||
let updated = false;
|
||||
const frontmatter = this.getFrontmatter(cache);
|
||||
if (!frontmatter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use replied or is_from_character (API may send either)
|
||||
const repliedRaw = state.replied ?? state.is_from_character;
|
||||
|
|
@ -641,7 +758,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
|
||||
// Update Replied? field - normalize boolean values for comparison
|
||||
const currentRepliedRaw = cache.frontmatter['Replied?'];
|
||||
const currentRepliedRaw = frontmatter['Replied?'];
|
||||
// Handle both boolean and string values
|
||||
const currentReplied = currentRepliedRaw === true || currentRepliedRaw === 'true' || currentRepliedRaw === 'True';
|
||||
// true = you've replied (no need to reply), false = need to reply
|
||||
|
|
@ -685,10 +802,10 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
|
||||
// Small delay to avoid checking during file creation
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await new Promise(resolve => window.setTimeout(resolve, 1000));
|
||||
|
||||
const cache = this.app.metadataCache.getFileCache(file);
|
||||
if (cache?.frontmatter) {
|
||||
if (this.getFrontmatter(cache)) {
|
||||
// Sync Is Active? to the tracker so unchecking removes the scene from MultiMuse
|
||||
await this.syncSceneActiveStatusToApi(file, cache);
|
||||
// Sync Participants to the tracker so "your turn" / Replied? use the correct count
|
||||
|
|
@ -721,7 +838,9 @@ export default class MultimuseObsidian extends Plugin {
|
|||
const primaryUserId = await this.getPrimaryUserId();
|
||||
if (!primaryUserId) return;
|
||||
|
||||
const threadId = this.extractThreadIdFromUrl(String(link));
|
||||
if (typeof link !== 'string') return;
|
||||
|
||||
const threadId = this.extractThreadIdFromUrl(link);
|
||||
const raw = cache.frontmatter?.['Is Active?'];
|
||||
const isActive = raw !== false && raw !== 'false';
|
||||
|
||||
|
|
@ -776,9 +895,15 @@ export default class MultimuseObsidian extends Plugin {
|
|||
const primaryUserId = await this.getPrimaryUserId();
|
||||
if (!primaryUserId) return;
|
||||
|
||||
const threadId = this.extractThreadIdFromUrl(String(link));
|
||||
if (typeof link !== 'string') return;
|
||||
|
||||
const threadId = this.extractThreadIdFromUrl(link);
|
||||
const raw = cache.frontmatter?.['Participants'];
|
||||
const participants = typeof raw === 'number' && raw >= 1 ? raw : parseInt(String(raw || '2'), 10);
|
||||
const participants = typeof raw === 'number' && raw >= 1
|
||||
? raw
|
||||
: typeof raw === 'string'
|
||||
? parseInt(raw, 10)
|
||||
: 2;
|
||||
if (isNaN(participants) || participants < 1) return;
|
||||
|
||||
try {
|
||||
|
|
@ -827,10 +952,10 @@ export default class MultimuseObsidian extends Plugin {
|
|||
return map;
|
||||
}
|
||||
|
||||
collectMarkdownFiles(fileOrFolder: any, files: TFile[]): void {
|
||||
collectMarkdownFiles(fileOrFolder: TAbstractFile, files: TFile[]): void {
|
||||
if (fileOrFolder instanceof TFile && fileOrFolder.extension === 'md') {
|
||||
files.push(fileOrFolder);
|
||||
} else if ('children' in fileOrFolder) {
|
||||
} else if (fileOrFolder instanceof TFolder) {
|
||||
for (const child of fileOrFolder.children) {
|
||||
this.collectMarkdownFiles(child, files);
|
||||
}
|
||||
|
|
@ -838,7 +963,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
|
||||
|
||||
getCharacterNames(frontmatter: any): string[] {
|
||||
getCharacterNames(frontmatter: FrontmatterData): string[] {
|
||||
const characters = frontmatter['Characters'];
|
||||
if (!characters) {
|
||||
return [];
|
||||
|
|
@ -879,7 +1004,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
|
||||
|
||||
async updateFrontmatter(file: TFile, key: string, value: any): Promise<void> {
|
||||
async updateFrontmatter(file: TFile, key: string, value: FrontmatterValue): Promise<void> {
|
||||
const content = await this.app.vault.read(file);
|
||||
|
||||
// Parse frontmatter
|
||||
|
|
@ -961,7 +1086,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.text);
|
||||
const data = parseJson<MusesListResponse>(response.text);
|
||||
muses = data.muses || [];
|
||||
console.log(`[MultimuseObsidian] Found ${muses.length} muse(s) from ${userIds.length} user(s)`);
|
||||
if (muses.length > 0) {
|
||||
|
|
@ -996,7 +1121,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
const selectedMuse = muses[selectedMuseIndex];
|
||||
|
||||
// Small delay to ensure previous modal is fully closed
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
await new Promise(resolve => window.setTimeout(resolve, 100));
|
||||
|
||||
// 3) Get Discord thread/channel link
|
||||
const threadUrl = await this.showInputPrompt('Enter Discord thread/channel URL');
|
||||
|
|
@ -1009,7 +1134,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
|
||||
// Small delay before opening location modal
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
await new Promise(resolve => window.setTimeout(resolve, 100));
|
||||
|
||||
// 4) Get location (RP folder) - pass muse name for context
|
||||
console.log(`[MultimuseObsidian] createNewScene: About to select location for muse "${selectedMuse.name}"`);
|
||||
|
|
@ -1027,7 +1152,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
|
||||
// 7) Create scene file
|
||||
const filePath = `${location}/${sceneName}.md`;
|
||||
const frontmatter: Record<string, any> = {
|
||||
const frontmatter: Record<string, FrontmatterValue> = {
|
||||
'Link': threadUrl,
|
||||
'Characters': [selectedMuse.name],
|
||||
'Participants': participants,
|
||||
|
|
@ -1089,80 +1214,37 @@ export default class MultimuseObsidian extends Plugin {
|
|||
// Mark this file as recently created to skip immediate checking
|
||||
this.recentlyCreatedFiles.add(filePath);
|
||||
// Remove from the set after 60 seconds (enough time for the scene to be registered and settled with the API)
|
||||
setTimeout(() => {
|
||||
window.setTimeout(() => {
|
||||
this.recentlyCreatedFiles.delete(filePath);
|
||||
console.log(`[MultimuseObsidian] Removed ${filePath} from recently created files - will now be checked by polling`);
|
||||
}, 60000);
|
||||
|
||||
// 8) Register with bot API and optionally create thread tracking
|
||||
// 8) Link the vault scene to the current Discord-side thread tracker.
|
||||
try {
|
||||
// Register scene
|
||||
// Log the extracted thread info for debugging
|
||||
console.debug(`Registering scene: threadId=${threadInfo.threadId}, guildId=${threadInfo.guildId}, channelId=${threadInfo.channelId}, url=${threadUrl}`);
|
||||
console.debug(`Tracking scene: threadId=${threadInfo.threadId}, guildId=${threadInfo.guildId}, channelId=${threadInfo.channelId}, url=${threadUrl}`);
|
||||
|
||||
// Convert IDs to strings to avoid JavaScript number precision loss
|
||||
// Discord IDs are larger than Number.MAX_SAFE_INTEGER, so we send them as strings
|
||||
// Use primary user ID for scene registration
|
||||
// Use primary user ID for thread tracking
|
||||
const primaryUserId = await this.getPrimaryUserId();
|
||||
if (!primaryUserId) {
|
||||
new Notice('Failed to get user ID from API key. Please check your API key in settings.');
|
||||
return;
|
||||
}
|
||||
|
||||
const registerResponse = await requestUrl({
|
||||
url: `${this.getBotApiUrl()}/api/v1/scenes/create`,
|
||||
method: 'POST',
|
||||
headers: this.getApiHeaders(),
|
||||
body: JSON.stringify({
|
||||
thread_id: threadInfo.threadId, // Send as string, let Python convert
|
||||
user_id: primaryUserId, // Send as string
|
||||
scene_path: createdFile.path,
|
||||
characters: [selectedMuse.name],
|
||||
participants: participants,
|
||||
is_active: true,
|
||||
guild_id: threadInfo.guildId || null // Send as string or null
|
||||
})
|
||||
const registerResponse = await this.trackThread({
|
||||
threadId: threadInfo.threadId,
|
||||
userId: primaryUserId,
|
||||
museName: selectedMuse.name,
|
||||
participants: participants,
|
||||
scenePath: createdFile.path,
|
||||
guildId: threadInfo.guildId || null,
|
||||
characters: [selectedMuse.name]
|
||||
});
|
||||
|
||||
console.debug(`Scene registration response: ${registerResponse.status} - ${registerResponse.text}`);
|
||||
console.debug(`Thread tracking response: ${registerResponse.status} - ${registerResponse.text}`);
|
||||
|
||||
if (registerResponse.status === 200) {
|
||||
// Scene registered successfully - proceed with optional operations
|
||||
|
||||
// Also create thread tracking (optional - don't fail if this errors)
|
||||
// This may fail if bot can't access the thread, which is okay
|
||||
try {
|
||||
const trackBody: Record<string, unknown> = {
|
||||
thread_id: threadInfo.threadId,
|
||||
user_id: primaryUserId,
|
||||
muse_name: selectedMuse.name,
|
||||
participants: participants,
|
||||
scene_path: createdFile.path,
|
||||
guild_id: threadInfo.guildId || null
|
||||
};
|
||||
if (selectedMuse.muse_id) {
|
||||
trackBody.muse_id = selectedMuse.muse_id;
|
||||
}
|
||||
const trackResponse = await requestUrl({
|
||||
url: `${this.getBotApiUrl()}/api/v1/threads/track`,
|
||||
method: 'POST',
|
||||
headers: this.getApiHeaders(),
|
||||
body: JSON.stringify(trackBody)
|
||||
});
|
||||
|
||||
if (trackResponse.status === 200) {
|
||||
console.debug('Thread tracking created successfully');
|
||||
} else {
|
||||
console.error(`Thread tracking failed: ${trackResponse.status} - ${trackResponse.text}`);
|
||||
}
|
||||
} catch (trackError: any) {
|
||||
// Thread tracking is optional - scene is already registered
|
||||
// Only log if it's not a 400 (which means bot can't access thread - expected)
|
||||
if (trackError?.status !== 400) {
|
||||
console.error('Thread tracking error (non-fatal):', trackError);
|
||||
}
|
||||
}
|
||||
|
||||
// Add to Base if configured
|
||||
try {
|
||||
if (this.settings.basePath) {
|
||||
|
|
@ -1176,29 +1258,20 @@ export default class MultimuseObsidian extends Plugin {
|
|||
await this.app.workspace.getLeaf(true).openFile(createdFile);
|
||||
} else if (registerResponse.status === 401) {
|
||||
// Authentication error - show helpful message
|
||||
this.handleApiError(registerResponse, 'createNewScene - register scene');
|
||||
new Notice('Scene created but failed to register with bot: Authentication failed. Check your API key.');
|
||||
this.handleApiError(registerResponse, 'createNewScene - track thread');
|
||||
new Notice('Scene created but failed to track with bot: Authentication failed. Check your API key.');
|
||||
} else {
|
||||
const errorText = registerResponse.text || 'Unknown error';
|
||||
console.error(`Failed to register scene: ${registerResponse.status} - ${errorText}`);
|
||||
new Notice(`Scene created but failed to register with bot: ${registerResponse.status}`);
|
||||
console.error(`Failed to track thread: ${registerResponse.status} - ${errorText}`);
|
||||
new Notice(`Scene created but failed to track with bot: ${registerResponse.status}`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
// Log the full error for debugging
|
||||
console.error('Error registering scene:', error);
|
||||
const errorMessage = error?.message || error?.text || 'Unknown error';
|
||||
console.error('Error tracking thread:', error);
|
||||
const errorMessage = getErrorMessage(error);
|
||||
console.error('Error details:', errorMessage);
|
||||
|
||||
// Only show error if scene registration itself failed
|
||||
// Thread tracking errors are handled above and shouldn't reach here
|
||||
if (error?.status === 400 && error?.message?.includes('threads/track')) {
|
||||
// This is a thread tracking error that slipped through - ignore it
|
||||
// Scene was already registered successfully
|
||||
new Notice(`Scene created: ${sceneName}`);
|
||||
await this.app.workspace.getLeaf(true).openFile(createdFile);
|
||||
} else {
|
||||
new Notice(`Scene created but failed to register with bot: ${errorMessage}`);
|
||||
}
|
||||
new Notice(`Scene created but failed to track with bot: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1232,7 +1305,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
const data = JSON.parse(response.text);
|
||||
const data = parseJson<TrackedThreadsResponse>(response.text);
|
||||
const threads = data.threads || [];
|
||||
|
||||
if (threads.length === 0) {
|
||||
|
|
@ -1248,7 +1321,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
|
||||
for (const thread of threads) {
|
||||
const threadId = String(thread.thread_id ?? '');
|
||||
const museName = thread.muse_name;
|
||||
const museName = thread.muse_name || 'Muse';
|
||||
const participants = thread.participants || 2;
|
||||
const existingScenePath = thread.scene_path;
|
||||
|
||||
|
|
@ -1298,7 +1371,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
const sceneName = thread.thread_name || `${museName} - Thread ${threadId}`;
|
||||
const filePath = `${location}/${sceneName}.md`;
|
||||
|
||||
const frontmatter: Record<string, any> = {
|
||||
const frontmatter: Record<string, FrontmatterValue> = {
|
||||
'Link': threadUrl,
|
||||
'Characters': [museName],
|
||||
'Participants': participants,
|
||||
|
|
@ -1360,30 +1433,25 @@ export default class MultimuseObsidian extends Plugin {
|
|||
// Mark this file as recently created to skip immediate checking
|
||||
this.recentlyCreatedFiles.add(filePath);
|
||||
// Remove from the set after 30 seconds (enough time for the scene to be registered with the API)
|
||||
setTimeout(() => {
|
||||
window.setTimeout(() => {
|
||||
this.recentlyCreatedFiles.delete(filePath);
|
||||
}, 30000);
|
||||
|
||||
// Register with bot
|
||||
// Use primary user ID for scene registration
|
||||
// Link the created note back to the current Discord-side thread tracker.
|
||||
const primaryUserId = await this.getPrimaryUserId();
|
||||
if (!primaryUserId) {
|
||||
console.error('Failed to get user ID from API key, skipping scene registration');
|
||||
console.error('Failed to get user ID from API key, skipping thread tracking update');
|
||||
continue;
|
||||
}
|
||||
|
||||
await requestUrl({
|
||||
url: `${this.getBotApiUrl()}/api/v1/scenes/create`,
|
||||
method: 'POST',
|
||||
headers: this.getApiHeaders(),
|
||||
body: JSON.stringify({
|
||||
thread_id: threadId, // Send as string to avoid precision loss
|
||||
user_id: primaryUserId, // Send as string
|
||||
scene_path: filePath,
|
||||
characters: [museName],
|
||||
participants: participants,
|
||||
guild_id: guildId || null, // Include guild_id if available
|
||||
})
|
||||
await this.trackThread({
|
||||
threadId: threadId,
|
||||
userId: primaryUserId,
|
||||
museName: museName,
|
||||
participants: Number(participants) || 2,
|
||||
scenePath: filePath,
|
||||
guildId: guildId ? String(guildId) : null,
|
||||
characters: [museName]
|
||||
});
|
||||
|
||||
// Add to Base if configured
|
||||
|
|
@ -1403,7 +1471,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
|
||||
// ========= BASE INTEGRATION =========
|
||||
|
||||
async addSceneToBase(file: TFile, frontmatter: any): Promise<void> {
|
||||
async addSceneToBase(file: TFile, frontmatter: FrontmatterData): Promise<void> {
|
||||
/**Add scene to Obsidian Base with characters as variables.*/
|
||||
if (!this.settings.basePath) return;
|
||||
|
||||
|
|
@ -1457,7 +1525,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
async updateBaseRecord(file: TFile, thread: any): Promise<void> {
|
||||
async updateBaseRecord(file: TFile, thread: TrackedThread): Promise<void> {
|
||||
/**Update existing Base record for a scene.*/
|
||||
if (!this.settings.basePath) return;
|
||||
|
||||
|
|
@ -1482,7 +1550,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
const lines = baseContent.split('\n');
|
||||
const updatedLines = lines.map(line => {
|
||||
if (line.includes(`| ${sceneName} |`)) {
|
||||
const characters = [thread.muse_name];
|
||||
const characters = [thread.muse_name || 'Muse'];
|
||||
const link = thread.guild_id && thread.guild_id !== '0'
|
||||
? `https://discord.com/channels/${thread.guild_id}/${thread.thread_id}`
|
||||
: `https://discord.com/channels/0/${thread.thread_id}`;
|
||||
|
|
@ -1597,19 +1665,17 @@ export default class MultimuseObsidian extends Plugin {
|
|||
return `${RP_ROOT}/${relPath}`;
|
||||
}
|
||||
|
||||
showSuggester(items: string[], values: any[], title?: string): Promise<number | null> {
|
||||
showSuggester<T>(items: string[], _values: T[], title?: string): Promise<number | null> {
|
||||
return new Promise((resolve) => {
|
||||
// Create a modal with buttons
|
||||
const modal = new (class extends Modal {
|
||||
selectedIndex: number | null = null;
|
||||
items: string[];
|
||||
values: any[];
|
||||
titleText: string;
|
||||
|
||||
constructor(app: App, items: string[], values: any[], titleText?: string) {
|
||||
constructor(app: App, items: string[], titleText?: string) {
|
||||
super(app);
|
||||
this.items = items;
|
||||
this.values = values;
|
||||
this.titleText = titleText || 'Select an option';
|
||||
// Set title in constructor to ensure it's set before modal opens
|
||||
this.titleEl.textContent = this.titleText;
|
||||
|
|
@ -1629,26 +1695,23 @@ export default class MultimuseObsidian extends Plugin {
|
|||
if (match) {
|
||||
const contextInfo = match[1];
|
||||
const infoEl = contentEl.createEl('div', {
|
||||
attr: {
|
||||
style: 'margin-bottom: 20px; padding: 10px; background: var(--background-modifier-border); border-radius: 4px;'
|
||||
}
|
||||
cls: 'multimuse-scene-context'
|
||||
});
|
||||
infoEl.createEl('strong', {
|
||||
text: `Creating scene file for: ${contextInfo}`,
|
||||
attr: { style: 'color: var(--text-normal); font-size: 1.1em;' }
|
||||
cls: 'multimuse-scene-context-title'
|
||||
});
|
||||
}
|
||||
const descEl = contentEl.createEl('p', {
|
||||
contentEl.createEl('p', {
|
||||
text: 'Choose where to create the scene file:',
|
||||
attr: { style: 'margin-top: 10px; margin-bottom: 15px; color: var(--text-muted);' }
|
||||
cls: 'multimuse-scene-location-desc'
|
||||
});
|
||||
}
|
||||
|
||||
this.items.forEach((item, index) => {
|
||||
const button = contentEl.createEl('button', {
|
||||
text: item,
|
||||
cls: 'mod-cta',
|
||||
attr: { style: 'width: 100%; margin: 5px 0;' }
|
||||
cls: ['mod-cta', 'multimuse-suggester-button']
|
||||
});
|
||||
button.onclick = () => {
|
||||
this.selectedIndex = index;
|
||||
|
|
@ -1660,11 +1723,11 @@ export default class MultimuseObsidian extends Plugin {
|
|||
onClose() {
|
||||
resolve(this.selectedIndex);
|
||||
}
|
||||
})(this.app, items, values, title);
|
||||
})(this.app, items, title);
|
||||
|
||||
console.log(`[MultimuseObsidian] showSuggester: Opening modal with ${items.length} items, title: ${title || 'Select an option'}`);
|
||||
// Use requestAnimationFrame to ensure modal opens after any previous modals are fully closed
|
||||
requestAnimationFrame(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
modal.open();
|
||||
});
|
||||
});
|
||||
|
|
@ -1682,9 +1745,9 @@ export default class MultimuseObsidian extends Plugin {
|
|||
this.titleEl.textContent = prompt;
|
||||
this.inputEl = this.contentEl.createEl('input', {
|
||||
type: 'text',
|
||||
value: defaultValue || ''
|
||||
value: defaultValue || '',
|
||||
cls: 'multimuse-input'
|
||||
});
|
||||
this.inputEl.style.width = '100%';
|
||||
this.inputEl.onkeydown = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.value = this.inputEl.value;
|
||||
|
|
@ -1722,8 +1785,9 @@ export default class MultimuseObsidian extends Plugin {
|
|||
new Notice('No frontmatter. Add a Link property (Discord thread URL) to this note.');
|
||||
return;
|
||||
}
|
||||
const link = cache.frontmatter['Link'];
|
||||
if (!link) {
|
||||
const frontmatter = this.getFrontmatter(cache);
|
||||
const link = frontmatter?.['Link'];
|
||||
if (typeof link !== 'string') {
|
||||
new Notice('No Link property. Add the Discord thread URL to frontmatter to use @ mentions.');
|
||||
return;
|
||||
}
|
||||
|
|
@ -1736,7 +1800,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
new Notice('API key required in plugin settings to fetch server members.');
|
||||
return;
|
||||
}
|
||||
let members: { id: string; username: string; display_name: string }[];
|
||||
let members: GuildMember[];
|
||||
try {
|
||||
const url = `${this.getBotApiUrl()}/api/v1/guilds/${threadInfo.guildId}/members`;
|
||||
const response = await requestUrl({
|
||||
|
|
@ -1750,7 +1814,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
}
|
||||
return;
|
||||
}
|
||||
const data = JSON.parse(response.text);
|
||||
const data = parseJson<GuildMembersResponse>(response.text);
|
||||
members = data.members || [];
|
||||
} catch (e) {
|
||||
console.error('[MultimuseObsidian] insertMention fetch error:', e);
|
||||
|
|
@ -1797,19 +1861,20 @@ export default class MultimuseObsidian extends Plugin {
|
|||
|
||||
// Get frontmatter
|
||||
const cache = this.app.metadataCache.getFileCache(file);
|
||||
if (!cache || !cache.frontmatter) {
|
||||
const frontmatter = this.getFrontmatter(cache);
|
||||
if (!frontmatter) {
|
||||
new Notice('File does not have frontmatter. Please add Link and Characters properties.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract link and characters
|
||||
const link = cache.frontmatter['Link'];
|
||||
if (!link) {
|
||||
const link = frontmatter?.['Link'];
|
||||
if (typeof link !== 'string') {
|
||||
new Notice('No Link property found in frontmatter. Please add a Discord thread URL.');
|
||||
return;
|
||||
}
|
||||
|
||||
const characters = this.getCharacterNames(cache.frontmatter);
|
||||
const characters = this.getCharacterNames(frontmatter);
|
||||
if (characters.length === 0) {
|
||||
new Notice('No Characters property found in frontmatter. Please add at least one character name.');
|
||||
return;
|
||||
|
|
@ -1861,7 +1926,7 @@ export default class MultimuseObsidian extends Plugin {
|
|||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.text);
|
||||
const data = parseJson<MusesListResponse>(response.text);
|
||||
muses = data.muses || [];
|
||||
} else {
|
||||
if (!this.handleApiError(response, 'sendSelectionAsMuse - fetch muses')) {
|
||||
|
|
@ -1909,17 +1974,16 @@ export default class MultimuseObsidian extends Plugin {
|
|||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.text);
|
||||
new Notice(`Message sent as ${selectedMuse}!`);
|
||||
} else {
|
||||
if (!this.handleApiError(response, 'sendSelectionAsMuse - post message')) {
|
||||
const errorData = JSON.parse(response.text);
|
||||
const errorData = parseJson<ApiErrorBody>(response.text);
|
||||
new Notice(`Failed to send message: ${errorData.message || response.status}`);
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error('Error sending message:', error);
|
||||
const errorMessage = error?.message || error?.text || 'Unknown error';
|
||||
const errorMessage = getErrorMessage(error);
|
||||
new Notice(`Failed to send message: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
|
@ -1938,7 +2002,9 @@ class MultimuseObsidianSettingTab extends PluginSettingTab {
|
|||
|
||||
containerEl.empty();
|
||||
|
||||
containerEl.createEl('h2', { text: 'Multimuse Obsidian Settings' });
|
||||
new Setting(containerEl)
|
||||
.setName('Multimuse Settings')
|
||||
.setHeading();
|
||||
|
||||
// Enable/Disable toggle
|
||||
new Setting(containerEl)
|
||||
|
|
@ -1997,7 +2063,9 @@ class MultimuseObsidianSettingTab extends PluginSettingTab {
|
|||
}));
|
||||
|
||||
// Scene Properties Tracking
|
||||
containerEl.createEl('h3', { text: 'Scene Properties' });
|
||||
new Setting(containerEl)
|
||||
.setName('Scene Properties')
|
||||
.setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Track Roleplay Property')
|
||||
|
|
@ -2059,8 +2127,8 @@ class MultimuseObsidianSettingTab extends PluginSettingTab {
|
|||
.setName('Detected User ID')
|
||||
.setDesc(`Your Discord user ID (automatically detected from API key): ${this.plugin.settings.cachedUserId}`)
|
||||
.addText(text => {
|
||||
text.setValue(this.plugin.settings.cachedUserId)
|
||||
.setDisabled(true);
|
||||
text.setValue(this.plugin.settings.cachedUserId);
|
||||
text.inputEl.disabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2084,14 +2152,16 @@ class MultimuseObsidianSettingTab extends PluginSettingTab {
|
|||
.setButtonText('Check Now')
|
||||
.setCta()
|
||||
.onClick(() => {
|
||||
this.plugin.checkAllThreads();
|
||||
void this.plugin.checkAllThreads();
|
||||
new Notice('Checking Discord threads...');
|
||||
}));
|
||||
|
||||
// Info section
|
||||
containerEl.createEl('hr');
|
||||
const infoEl = containerEl.createEl('div');
|
||||
infoEl.createEl('h3', { text: 'How It Works' });
|
||||
new Setting(infoEl)
|
||||
.setName('How It Works')
|
||||
.setHeading();
|
||||
infoEl.createEl('p', { text: 'This plugin queries the Multimuse API to check if your scene files match tracked threads and updates the "Replied?" and "Participants" fields.' });
|
||||
infoEl.createEl('p', { text: '• Scenes are matched by Link (thread_id) and Characters properties' });
|
||||
infoEl.createEl('p', { text: '• If scene matches a tracked thread: Updates Replied? only (true = you replied, false = need to reply). Participants is always editable in frontmatter.' });
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"id": "multimuse-obsidian",
|
||||
"name": "Multimuse Obsidian",
|
||||
"version": "1.6.1",
|
||||
"minAppVersion": "1.0.0",
|
||||
"description": "Roleplay sync tool for use with the MultiMuse bot in discord",
|
||||
"id": "multimuse-tracker",
|
||||
"name": "Multimuse Tracker",
|
||||
"version": "1.10.0",
|
||||
"minAppVersion": "1.4.0",
|
||||
"description": "Roleplay sync tool for use with the MultiMuse bot in discord.",
|
||||
"author": "BackstagePass Group",
|
||||
"authorUrl": "",
|
||||
"fundingUrl": "",
|
||||
|
|
|
|||
354
package-lock.json
generated
354
package-lock.json
generated
|
|
@ -1,19 +1,18 @@
|
|||
{
|
||||
"name": "discord-scene-tracker",
|
||||
"version": "1.0.0",
|
||||
"name": "multimuse-tracker",
|
||||
"version": "1.10.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "discord-scene-tracker",
|
||||
"version": "1.0.0",
|
||||
"name": "multimuse-tracker",
|
||||
"version": "1.10.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
"@typescript-eslint/eslint-plugin": "5.29.0",
|
||||
"@typescript-eslint/parser": "5.29.0",
|
||||
"builtin-modules": "3.3.0",
|
||||
"esbuild": "0.17.3",
|
||||
"esbuild": "^0.28.0",
|
||||
"obsidian": "latest",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.7.4"
|
||||
|
|
@ -44,10 +43,27 @@
|
|||
"w3c-keyname": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
|
||||
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz",
|
||||
"integrity": "sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
|
||||
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -58,13 +74,13 @@
|
|||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.3.tgz",
|
||||
"integrity": "sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -75,13 +91,13 @@
|
|||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.3.tgz",
|
||||
"integrity": "sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -92,13 +108,13 @@
|
|||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.3.tgz",
|
||||
"integrity": "sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -109,13 +125,13 @@
|
|||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.3.tgz",
|
||||
"integrity": "sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -126,13 +142,13 @@
|
|||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.3.tgz",
|
||||
"integrity": "sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -143,13 +159,13 @@
|
|||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.3.tgz",
|
||||
"integrity": "sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -160,13 +176,13 @@
|
|||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.3.tgz",
|
||||
"integrity": "sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
|
||||
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -177,13 +193,13 @@
|
|||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.3.tgz",
|
||||
"integrity": "sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -194,13 +210,13 @@
|
|||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.3.tgz",
|
||||
"integrity": "sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
|
||||
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
|
@ -211,13 +227,13 @@
|
|||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.3.tgz",
|
||||
"integrity": "sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
|
||||
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
|
|
@ -228,13 +244,13 @@
|
|||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.3.tgz",
|
||||
"integrity": "sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
|
||||
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
|
|
@ -245,13 +261,13 @@
|
|||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.3.tgz",
|
||||
"integrity": "sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
|
||||
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
|
|
@ -262,13 +278,13 @@
|
|||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.3.tgz",
|
||||
"integrity": "sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
|
||||
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
|
|
@ -279,13 +295,13 @@
|
|||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.3.tgz",
|
||||
"integrity": "sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
|
||||
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
|
|
@ -296,13 +312,13 @@
|
|||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.3.tgz",
|
||||
"integrity": "sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -313,13 +329,30 @@
|
|||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.3.tgz",
|
||||
"integrity": "sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -330,13 +363,30 @@
|
|||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.3.tgz",
|
||||
"integrity": "sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -347,13 +397,30 @@
|
|||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.3.tgz",
|
||||
"integrity": "sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -364,13 +431,13 @@
|
|||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.3.tgz",
|
||||
"integrity": "sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -381,13 +448,13 @@
|
|||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.3.tgz",
|
||||
"integrity": "sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
|
||||
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
|
@ -398,13 +465,13 @@
|
|||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.3.tgz",
|
||||
"integrity": "sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -415,7 +482,7 @@
|
|||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
|
|
@ -830,9 +897,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"version": "6.15.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
|
||||
"integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -897,9 +964,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -920,19 +987,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/builtin-modules": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
|
||||
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
|
|
@ -1061,9 +1115,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.17.3",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.3.tgz",
|
||||
"integrity": "sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==",
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
|
||||
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
|
|
@ -1071,31 +1125,35 @@
|
|||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/android-arm": "0.17.3",
|
||||
"@esbuild/android-arm64": "0.17.3",
|
||||
"@esbuild/android-x64": "0.17.3",
|
||||
"@esbuild/darwin-arm64": "0.17.3",
|
||||
"@esbuild/darwin-x64": "0.17.3",
|
||||
"@esbuild/freebsd-arm64": "0.17.3",
|
||||
"@esbuild/freebsd-x64": "0.17.3",
|
||||
"@esbuild/linux-arm": "0.17.3",
|
||||
"@esbuild/linux-arm64": "0.17.3",
|
||||
"@esbuild/linux-ia32": "0.17.3",
|
||||
"@esbuild/linux-loong64": "0.17.3",
|
||||
"@esbuild/linux-mips64el": "0.17.3",
|
||||
"@esbuild/linux-ppc64": "0.17.3",
|
||||
"@esbuild/linux-riscv64": "0.17.3",
|
||||
"@esbuild/linux-s390x": "0.17.3",
|
||||
"@esbuild/linux-x64": "0.17.3",
|
||||
"@esbuild/netbsd-x64": "0.17.3",
|
||||
"@esbuild/openbsd-x64": "0.17.3",
|
||||
"@esbuild/sunos-x64": "0.17.3",
|
||||
"@esbuild/win32-arm64": "0.17.3",
|
||||
"@esbuild/win32-ia32": "0.17.3",
|
||||
"@esbuild/win32-x64": "0.17.3"
|
||||
"@esbuild/aix-ppc64": "0.28.0",
|
||||
"@esbuild/android-arm": "0.28.0",
|
||||
"@esbuild/android-arm64": "0.28.0",
|
||||
"@esbuild/android-x64": "0.28.0",
|
||||
"@esbuild/darwin-arm64": "0.28.0",
|
||||
"@esbuild/darwin-x64": "0.28.0",
|
||||
"@esbuild/freebsd-arm64": "0.28.0",
|
||||
"@esbuild/freebsd-x64": "0.28.0",
|
||||
"@esbuild/linux-arm": "0.28.0",
|
||||
"@esbuild/linux-arm64": "0.28.0",
|
||||
"@esbuild/linux-ia32": "0.28.0",
|
||||
"@esbuild/linux-loong64": "0.28.0",
|
||||
"@esbuild/linux-mips64el": "0.28.0",
|
||||
"@esbuild/linux-ppc64": "0.28.0",
|
||||
"@esbuild/linux-riscv64": "0.28.0",
|
||||
"@esbuild/linux-s390x": "0.28.0",
|
||||
"@esbuild/linux-x64": "0.28.0",
|
||||
"@esbuild/netbsd-arm64": "0.28.0",
|
||||
"@esbuild/netbsd-x64": "0.28.0",
|
||||
"@esbuild/openbsd-arm64": "0.28.0",
|
||||
"@esbuild/openbsd-x64": "0.28.0",
|
||||
"@esbuild/openharmony-arm64": "0.28.0",
|
||||
"@esbuild/sunos-x64": "0.28.0",
|
||||
"@esbuild/win32-arm64": "0.28.0",
|
||||
"@esbuild/win32-ia32": "0.28.0",
|
||||
"@esbuild/win32-x64": "0.28.0"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
|
|
@ -1456,9 +1514,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
||||
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
|
|
@ -1777,9 +1835,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
|
@ -1942,9 +2000,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
|
|||
10
package.json
10
package.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "multimuse-obsidian",
|
||||
"version": "1.3.0",
|
||||
"description": "Obsidian plugin for MultiMuse bot integration - track Discord threads and send messages as muses",
|
||||
"name": "multimuse-tracker",
|
||||
"version": "1.10.0",
|
||||
"description": "MultiMuse bot integration for tracking Discord threads and sending messages as muses",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"dev": "node esbuild.config.mjs",
|
||||
|
|
@ -15,11 +15,9 @@
|
|||
"@types/node": "^16.11.6",
|
||||
"@typescript-eslint/eslint-plugin": "5.29.0",
|
||||
"@typescript-eslint/parser": "5.29.0",
|
||||
"builtin-modules": "3.3.0",
|
||||
"esbuild": "0.17.3",
|
||||
"esbuild": "^0.28.0",
|
||||
"obsidian": "latest",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.7.4"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
27
styles.css
27
styles.css
|
|
@ -17,3 +17,30 @@
|
|||
border-top: none;
|
||||
}
|
||||
|
||||
.multimuse-scene-context {
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
background: var(--background-modifier-border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.multimuse-scene-context-title {
|
||||
color: var(--text-normal);
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.multimuse-scene-location-desc {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.multimuse-suggester-button,
|
||||
.multimuse-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.multimuse-suggester-button {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue