Add plannotator extension v0.19.10
This commit is contained in:
244
extensions/plannotator/generated/integrations-common.ts
Normal file
244
extensions/plannotator/generated/integrations-common.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
// @generated — DO NOT EDIT. Source: packages/shared/integrations-common.ts
|
||||
import { existsSync, readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
// --- Types ---
|
||||
|
||||
export interface ObsidianConfig {
|
||||
vaultPath: string;
|
||||
folder: string;
|
||||
plan: string;
|
||||
filenameFormat?: string; // Custom format string, e.g. '{YYYY}-{MM}-{DD} - {title}'
|
||||
filenameSeparator?: "space" | "dash" | "underscore"; // Replace spaces in filename
|
||||
}
|
||||
|
||||
export interface BearConfig {
|
||||
plan: string;
|
||||
customTags?: string;
|
||||
tagPosition?: "prepend" | "append";
|
||||
}
|
||||
|
||||
export interface OctarineConfig {
|
||||
plan: string;
|
||||
workspace: string;
|
||||
folder: string;
|
||||
}
|
||||
|
||||
export interface IntegrationResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect Obsidian vaults by reading Obsidian's config file
|
||||
* Returns array of vault paths found on the system
|
||||
*/
|
||||
export function detectObsidianVaults(): string[] {
|
||||
try {
|
||||
const home = process.env.HOME || process.env.USERPROFILE || "";
|
||||
let configPath: string;
|
||||
|
||||
// Platform-specific config locations
|
||||
if (process.platform === "darwin") {
|
||||
configPath = join(
|
||||
home,
|
||||
"Library/Application Support/obsidian/obsidian.json",
|
||||
);
|
||||
} else if (process.platform === "win32") {
|
||||
const appData = process.env.APPDATA || join(home, "AppData/Roaming");
|
||||
configPath = join(appData, "obsidian/obsidian.json");
|
||||
} else {
|
||||
// Linux
|
||||
configPath = join(home, ".config/obsidian/obsidian.json");
|
||||
}
|
||||
|
||||
if (!existsSync(configPath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const configContent = readFileSync(configPath, "utf-8");
|
||||
const config = JSON.parse(configContent);
|
||||
|
||||
if (!config.vaults || typeof config.vaults !== "object") {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Extract vault paths, filter to ones that exist
|
||||
const vaults: string[] = [];
|
||||
for (const vaultId of Object.keys(config.vaults)) {
|
||||
const vault = config.vaults[vaultId];
|
||||
if (vault.path && existsSync(vault.path)) {
|
||||
vaults.push(vault.path);
|
||||
}
|
||||
}
|
||||
|
||||
return vaults;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// --- Frontmatter and Filename Generation ---
|
||||
|
||||
/**
|
||||
* Generate frontmatter for the note
|
||||
*/
|
||||
export function generateFrontmatter(tags: string[]): string {
|
||||
const now = new Date().toISOString();
|
||||
const tagList = tags.map((t) => t.toLowerCase()).join(", ");
|
||||
return `---
|
||||
created: ${now}
|
||||
source: plannotator
|
||||
tags: [${tagList}]
|
||||
---`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract title from markdown (first H1 heading)
|
||||
*/
|
||||
export function extractTitle(markdown: string): string {
|
||||
const h1Match = markdown.match(
|
||||
/^#\s+(?:Implementation\s+Plan:|Plan:)?\s*(.+)$/im,
|
||||
);
|
||||
if (h1Match) {
|
||||
// Clean up the title for use as filename
|
||||
return h1Match[1]
|
||||
.trim()
|
||||
.replace(/[<>:"/\\|?*(){}\[\]#~`]/g, "") // Remove invalid/problematic filename chars
|
||||
.replace(/\s+/g, " ") // Normalize whitespace
|
||||
.trim() // Re-trim after stripping
|
||||
.slice(0, 50); // Limit length
|
||||
}
|
||||
return "Plan";
|
||||
}
|
||||
|
||||
/** Default filename format matching original behavior */
|
||||
export const DEFAULT_FILENAME_FORMAT =
|
||||
"{title} - {Mon} {D}, {YYYY} {h}-{mm}{ampm}";
|
||||
|
||||
/**
|
||||
* Generate filename from a format string with variable substitution.
|
||||
*
|
||||
* Supported variables:
|
||||
* {title} - Plan title from first H1 heading
|
||||
* {YYYY} - 4-digit year
|
||||
* {MM} - 2-digit month (01-12)
|
||||
* {DD} - 2-digit day (01-31)
|
||||
* {Mon} - Abbreviated month name (Jan, Feb, ...)
|
||||
* {D} - Day without leading zero
|
||||
* {HH} - 2-digit hour, 24h (00-23)
|
||||
* {h} - Hour without leading zero, 12h
|
||||
* {hh} - 2-digit hour, 12h (01-12)
|
||||
* {mm} - 2-digit minutes (00-59)
|
||||
* {ss} - 2-digit seconds (00-59)
|
||||
* {ampm} - am/pm
|
||||
*
|
||||
* Default format: '{title} - {Mon} {D}, {YYYY} {h}-{mm}{ampm}'
|
||||
* Example output: 'User Authentication - Jan 2, 2026 2-30pm.md'
|
||||
*/
|
||||
export function generateFilename(
|
||||
markdown: string,
|
||||
format?: string,
|
||||
separator?: "space" | "dash" | "underscore",
|
||||
): string {
|
||||
const title = extractTitle(markdown);
|
||||
const now = new Date();
|
||||
|
||||
const months = [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
];
|
||||
|
||||
const hour24 = now.getHours();
|
||||
const hour12 = hour24 % 12 || 12;
|
||||
const ampm = hour24 >= 12 ? "pm" : "am";
|
||||
|
||||
const vars: Record<string, string> = {
|
||||
title,
|
||||
YYYY: String(now.getFullYear()),
|
||||
MM: String(now.getMonth() + 1).padStart(2, "0"),
|
||||
DD: String(now.getDate()).padStart(2, "0"),
|
||||
Mon: months[now.getMonth()],
|
||||
D: String(now.getDate()),
|
||||
HH: String(hour24).padStart(2, "0"),
|
||||
h: String(hour12),
|
||||
hh: String(hour12).padStart(2, "0"),
|
||||
mm: String(now.getMinutes()).padStart(2, "0"),
|
||||
ss: String(now.getSeconds()).padStart(2, "0"),
|
||||
ampm,
|
||||
};
|
||||
|
||||
const template = format?.trim() || DEFAULT_FILENAME_FORMAT;
|
||||
const result = template.replace(
|
||||
/\{(\w+)\}/g,
|
||||
(match, key) => vars[key] ?? match,
|
||||
);
|
||||
|
||||
// Sanitize: remove characters invalid in filenames
|
||||
let sanitized = result
|
||||
.replace(/[<>:"/\\|?*]/g, "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
|
||||
// Apply separator preference (replace spaces with dash or underscore)
|
||||
if (separator === "dash") {
|
||||
sanitized = sanitized.replace(/ /g, "-");
|
||||
} else if (separator === "underscore") {
|
||||
sanitized = sanitized.replace(/ /g, "_");
|
||||
}
|
||||
|
||||
return sanitized.endsWith(".md") ? sanitized : `${sanitized}.md`;
|
||||
}
|
||||
|
||||
// --- Bear Integration ---
|
||||
|
||||
export function stripH1(plan: string): string {
|
||||
return plan.replace(/^#\s+.+\n?/m, "").trimStart();
|
||||
}
|
||||
|
||||
export function buildHashtags(
|
||||
customTags: string | undefined,
|
||||
autoTags: string[],
|
||||
): string {
|
||||
if (customTags?.trim()) {
|
||||
return customTags
|
||||
.split(",")
|
||||
.map((t) => `#${t.trim()}`)
|
||||
.filter((t) => t !== "#")
|
||||
.join(" ");
|
||||
}
|
||||
return autoTags.map((t) => `#${t}`).join(" ");
|
||||
}
|
||||
|
||||
export function buildBearContent(
|
||||
body: string,
|
||||
hashtags: string,
|
||||
tagPosition: "prepend" | "append",
|
||||
): string {
|
||||
return tagPosition === "prepend"
|
||||
? `${hashtags}\n\n${body}`
|
||||
: `${body}\n\n${hashtags}`;
|
||||
}
|
||||
|
||||
// --- Octarine Integration ---
|
||||
|
||||
/**
|
||||
* Generate YAML frontmatter for an Octarine note.
|
||||
* Uses Octarine's property format (list-style tags, Status, Author, Last Edited).
|
||||
*/
|
||||
export function generateOctarineFrontmatter(tags: string[]): string {
|
||||
const now = new Date().toISOString().slice(0, 16); // YYYY-MM-DDTHH:MM
|
||||
const tagLines = tags.map((t) => ` - ${t.toLowerCase()}`).join("\n");
|
||||
return `---\ntags:\n${tagLines}\nStatus: Draft\nAuthor: plannotator\nLast Edited: ${now}\n---`;
|
||||
}
|
||||
Reference in New Issue
Block a user