### Query Files using Dataview API via ZettelFlow (JavaScript) Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Queries files using the Dataview API, integrated through ZettelFlow's external API. This requires the Dataview plugin to be installed. The example demonstrates filtering pages with the '#project' tag and sorting them by priority. ```javascript // Example: Query files using Dataview const results = zf.external.dv.pages('#project') .where(p => p.status !== "Complete") .sort(p => p.priority, 'desc'); ``` -------------------------------- ### Calculate Completion Percentage (JavaScript) Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/vault-hooks/property-hooks/examples.md This JavaScript hook calculates a 'completion_percentage' based on 'completed_items' and 'total_items' in the frontmatter. It also updates the 'status' property to 'Complete', 'In Progress', or 'Not Started' based on the calculated percentage. It handles cases where 'completed_items' or 'total_items' might be missing by defaulting them to 0 and 1 respectively. ```javascript // When total items or completed items change, calculate percentage const frontmatter = event.request.frontmatter; const completed = frontmatter.completed_items || 0; const total = frontmatter.total_items || 1; const percentage = Math.round((completed / total) * 100); event.response.frontmatter.completion_percentage = percentage; // Also update status based on percentage if (percentage >= 100) { event.response.frontmatter.status = 'Complete'; } else if (percentage > 0) { event.response.frontmatter.status = 'In Progress'; } else { event.response.frontmatter.status = 'Not Started'; } ``` -------------------------------- ### Calculate Reading Time and Category (JavaScript) Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/vault-hooks/property-hooks/examples.md This JavaScript hook estimates reading time in minutes based on the 'word_count' frontmatter property, assuming an average reading speed of 200 words per minute. It then assigns a 'reading_category' ('Quick Read', 'Short Read', 'Medium Read', 'Long Read') based on the estimated reading time. ```javascript // When word_count changes const wordCount = parseInt(event.request.newValue) || 0; // Assume average reading speed of 200 words per minute const readingMinutes = Math.ceil(wordCount / 200); event.response.frontmatter.reading_time = readingMinutes; // Also update a reading time category if (readingMinutes <= 1) { event.response.frontmatter.reading_category = 'Quick Read'; } else if (readingMinutes <= 5) { event.response.frontmatter.reading_category = 'Short Read'; } else if (readingMinutes <= 15) { event.response.frontmatter.reading_category = 'Medium Read'; } else { event.response.frontmatter.reading_category = 'Long Read'; } ``` -------------------------------- ### External API - Dataview Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Provides access to the complete Dataview API if the Dataview plugin is installed. ```APIDOC ## External API - Dataview The complete [Dataview API](https://github.com/blacksmithgu/obsidian-dataview/blob/master/src/api/plugin-api.ts) (available if the Dataview plugin is installed). ### Request Example ```javascript // Query files using Dataview const results = zf.external.dv.pages('#project') .where(p => p.status !== "Complete") .sort(p => p.priority, 'desc'); ``` ``` -------------------------------- ### ZettelFlow Script API: Vault and Note Operations in TypeScript Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt This TypeScript example shows how to use the zf API within ZettelFlow scripts for common tasks. It includes resolving folders, obtaining files, filtering based on frontmatter, modifying note content, and managing frontmatter properties. The API also allows data sharing across actions via context. ```typescript // Example script in a Script action or Dynamic Selector const { vault, scripts } = zf; // Resolve folder and get files const folder = vault.resolveTFolder("Projects/Active"); const files = vault.obtainFilesFrom(folder, ["md"]); // Get .md files // Filter files by frontmatter property const projectFiles = files.filter(file => { const cache = app.metadataCache.getFileCache(file); return cache?.frontmatter?.type === "project"; }); // Modify content in note builder content.modify("{{title}}", note.getTitle()); content.addFrontMatter({ created: moment().format("YYYY-MM-DD"), tags: ["project", "active"] }); // Add data to shared context (available across all actions in flow) context.selectedProject = projectFiles[0].basename; context.fileCount = files.length; // Return options for Dynamic Selector return projectFiles.map(file => ({ label: file.basename, value: file.path })); ``` -------------------------------- ### External API - Templater Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Provides access to user scripts from the Templater plugin if it is installed. ```APIDOC ## External API - Templater User scripts from the [Templater plugin](https://silentvoid13.github.io/Templater/user-functions/script-user-functions.html) (available if Templater is installed). ### Request Example ```javascript // Use a Templater user script const result = zf.external.tp.user.myScript(); ``` ``` -------------------------------- ### Get Note Target Folder - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet shows how to retrieve the currently set target folder for the note using `note.getTargetFolder()`. This can be useful for checking or reusing folder paths. ```javascript const folder = note.getTargetFolder(); ``` -------------------------------- ### Use Templater User Script via ZettelFlow External API (JavaScript) Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Integrates with the Templater plugin to execute user scripts. This functionality is available through ZettelFlow's external API if the Templater plugin is installed. The example shows calling a Templater user script named 'myScript'. ```javascript // Example: Use a Templater user script const result = zf.external.tp.user.myScript(); ``` -------------------------------- ### Get Tags from Note - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet shows how to retrieve all tags currently present in a note's frontmatter using `content.getTags()`. The function returns an array of strings. ```javascript const tags = content.getTags(); ``` -------------------------------- ### Context Object for State Management (JavaScript) Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Demonstrates the use of the 'context' object, a shared variable available in Scripts and Hooks. It allows for storing and retrieving data across different execution steps, facilitating state management within ZettelFlow operations. The example shows storing and accessing a simple data object. ```javascript // Store data in one step context.myData = { value: 42 }; // Use it in another step console.log(context.myData.value); // 42 ``` -------------------------------- ### TypeScript: FlowImpl Class for Navigating Flow Nodes Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt The `FlowImpl` class in TypeScript manages flow instances, enabling traversal of the node graph. It initializes by processing canvas data, filtering out link nodes, and processing file nodes with '.js' extensions. The class provides methods to get children of a node, either through direct connections (edges) or group membership, and to identify root nodes marked with 'root: true' in their configuration. Dependencies include `AllCanvasNodeData`, `CanvasData`, `TFile`, `FlowNode`, `YamlService`, `FileService`, and `FrontmatterService`. ```typescript export class FlowImpl implements Flow { private nodes: Map; constructor(public data: CanvasData, private file: TFile) { this.nodes = data.nodes .filter(node => node.type !== "link") .reduce((map, obj) => { if (obj.type === "file" && obj.file.endsWith(".js")) { obj.extension = "js"; } map.set(obj.id, obj); return map; }, new Map()); } // Get children of a node (via arrows or group membership) childrensOf = async (nodeId: string) => { const node = this.nodes.get(nodeId); if (node?.type !== "group") { const { edges } = this.data; const childrenKeys = edges .filter(edge => edge.fromNode === nodeId) .map(edge => ({ key: edge.toNode, tooltip: edge.label })); return this.nodesFrom(childrenKeys); } else { const childNodes = findDirectChildren(node, this.data.nodes); const childrenKeys = childNodes.map(child => ({ key: child.id, tooltip: `Child of ${node.label}` }) ); return this.nodesFrom(childrenKeys); } } // Find all root nodes (marked with root: true) rootNodes = async () => { const rootNodes: FlowNode[] = []; const { nodes } = this.data; for (const node of nodes) { if (node.type === "text" || node.type === "group") { const textNode = YamlService.instance(node.zettelflowConfig); if (textNode.isRoot()) { const flowNode = textNode.getZettelFlowSettings(); rootNodes.push(this.populateNode(node, flowNode)); } } else if (node.type === "file") { const file = await FileService.getFile(node.file); if (!file) continue; const fileNode = FrontmatterService.instance(file); if (fileNode.equals("zettelFlowSettings.root", true)) { const flowNode = fileNode.getZettelFlowSettings(); rootNodes.push(this.populateNode(node, flowNode)); } } } return rootNodes; } } ``` -------------------------------- ### Get Frontmatter from Note - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet illustrates how to retrieve the entire frontmatter object of a note using `content.getFrontMatter()`. The function returns a Record object representing the frontmatter. ```javascript const frontmatter = content.getFrontMatter(); ``` -------------------------------- ### Get Note Title - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet demonstrates how to retrieve the current title of the note using `note.getTitle()`. This can be used for logging or conditional logic within scripts. ```javascript const title = note.getTitle(); ``` -------------------------------- ### Task Status Based on Due Date (JavaScript) Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/vault-hooks/property-hooks/examples.md This JavaScript hook updates a task's 'status' and 'priority' based on its 'due_date'. It calculates the difference in days between the due date and the current date to determine if the task is 'Overdue', 'Due Today', 'Upcoming', or 'Scheduled', assigning appropriate priorities. ```javascript // When due_date changes const now = new Date(); const dueDate = new Date(event.request.newValue); // Calculate days difference const diffTime = dueDate.getTime() - now.getTime(); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); // Update status and priority based on due date if (diffDays < 0) { event.response.frontmatter.status = 'Overdue'; event.response.frontmatter.priority = 'High'; } else if (diffDays === 0) { event.response.frontmatter.status = 'Due Today'; event.response.frontmatter.priority = 'High'; } else if (diffDays <= 3) { event.response.frontmatter.status = 'Upcoming'; event.response.frontmatter.priority = 'Medium'; } else { event.response.frontmatter.status = 'Scheduled'; event.response.frontmatter.priority = 'Low'; } ``` -------------------------------- ### Update Modified Date on Status Change (JavaScript) Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/vault-hooks/property-hooks/examples.md This JavaScript hook automatically updates a 'modified' frontmatter property with the current ISO timestamp whenever a 'status' property changes. It logs the change from old to new status and the updated modified date to the console for debugging. ```javascript // When status changes, update the modified date const now = new Date().toISOString(); event.response.frontmatter.modified = now; console.log(`Status changed from ${event.request.oldValue} to ${event.request.newValue}, updated modified date to ${now}`); ``` -------------------------------- ### TypeScript ZettelFlow Plugin Settings Interface and Defaults Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt Defines the ZettelFlow plugin settings interface (ZettelFlowSettings) in TypeScript, outlining various configuration options. It also provides default settings (DEFAULT_SETTINGS) for initialization, covering aspects like logging, unique prefixes, and folder paths. ```typescript export interface ZettelFlowSettings { loggerEnabled: boolean; logLevel: string; uniquePrefixEnabled: boolean; uniquePrefix: string; // Format: "YYYYMMDDHHmmss" tableOfContentEnabled: boolean; ribbonCanvas: string; // Main flow canvas path editorCanvas: string; // Editor mode canvas path jsLibraryFolderPath: string; // Folder for reusable JS libraries foldersFlowsPath: string; // Folder for folder-specific flows installedTemplates: { steps: Record; actions: Record; }; communitySettings: { markdownTemplateFolder: string; url: string; token?: string; }; hooks: { properties: Record; folderFlowPath: string; }; } export const DEFAULT_SETTINGS: Partial = { loggerEnabled: false, uniquePrefixEnabled: false, uniquePrefix: "YYYYMMDDHHmmss", foldersFlowsPath: "_ZettelFlow/folders", tableOfContentEnabled: true, installedTemplates: { steps: {}, actions: {} }, communitySettings: { url: "http://127.0.0.1:8000", markdownTemplateFolder: "_ZettelFlowMdTemplates", }, hooks: { properties: {}, folderFlowPath: "_ZettelFlow/hooks" } }; ``` -------------------------------- ### Display Tasks in Progress with DataviewJS Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/steps/markdown/Daily Note Template.md This DataviewJS snippet queries Obsidian notes tagged with '#task' or '#epic' that have a status of '🧐 On progress'. It then iterates through these tasks, displaying their deadlines, tags, purposes, and any unfinished sub-tasks. The output is sorted by deadline. ```dataviewjs const tasks = dv.pages('#task or #epic') .where(p => p.status === "🧐 On progress") .sort(p => p.deadline, 'asc'); for (const task of tasks) { dv.header(4, task.file.link); // Mostrar deadline si existe if (task.deadline) { dv.paragraph(`**Deadline:** ${task.deadline.toRelative()}`); } if (task.tags) { const tagString = task.tags.join(', '); dv.paragraph(`**Tags:** ${tagString} (${task.tags.includes('task') ? 'Task' : 'Epic'})`); } if (task.purpose) { dv.paragraph(`**Purpose:** ${task.purpose}`); } if (task.file.tasks.length > 0) { const incompleteTasks = task.file.tasks.filter(t => !t.completed); if (incompleteTasks.length > 0) { dv.paragraph("**Unfinished Tasks:**"); dv.taskList(incompleteTasks, task.file.path); } } dv.paragraph('---'); } ``` -------------------------------- ### Generate Folder Options with JavaScript for Obsidian Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/DynamicSelector.md This script generates dynamic options for a selector by retrieving all folders in the Obsidian vault. It requires the Obsidian API's `app.vault.getAllFolders()` method and should return an array of string tuples, where each tuple is a [key, display label] pair. This enables context-aware selection based on the vault's folder structure. ```javascript const testFolders = app.vault.getAllFolders(); // Add your filtering logic here // ... return testFolders.map(folder => [folder.path,folder.name]); ``` -------------------------------- ### Build Notes from Flow Steps (TypeScript) Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt The NoteBuilder class processes flow steps and their actions to generate final note content. It handles creating new notes or inserting content into the editor, managing file creation, frontmatter processing, and executing defined actions. Error handling is included for file operations and state management. ```typescript import { NoteDTO, ContentDTO } from './model'; import { actionsStore } from 'architecture/api'; import { FileService, FrontmatterService } from 'architecture/plugin'; export class NoteBuilder { public context = {}; public note: NoteDTO; private content: ContentDTO; constructor() { this.note = new NoteDTO(); this.content = new ContentDTO(); } public async build(modal: SelectorMenuModal, actions: NoteBuilderStateActions) { if (modal.isEditor()) { return await this.buildEditor(modal); } else { return await this.buildNewNote(); } } private async buildNewNote() { try { VaultStateManager.INSTANCE.freeze(); this.note.setTitle(this.buildFilename()); await this.buildNote(); const generatedFile = await FileService.createFile( this.note.getFinalPath(), this.content.get(), false ); await FrontmatterService.instance(generatedFile) .processTypedFrontMatter(this.content); await this.postProcess(generatedFile); return generatedFile.path; } catch (error) { this.content.reset(); const potentialFile = await FileService.getFile(this.note.getFinalPath(), false); if (potentialFile) await FileService.deleteFile(potentialFile); VaultStateManager.INSTANCE.defrost(); throw error; } finally { VaultStateManager.INSTANCE.processFinished(this.note.getFinalPath()); } } private async buildNote() { // Process all paths (template files) for (const [, path] of this.note.getPaths()) { const file = await FileService.getFile(path); if (!file) continue; const service = FrontmatterService.instance(file); const frontmatter = service.getFrontmatter(); if (TypeService.isObject(frontmatter)) { this.content.addFrontMatter(frontmatter); } this.content.add(await service.getContent()); } // Execute all actions for (const [, element] of this.note.getElements()) { await actionsStore.getAction(element.type) .execute({ element, content: this.content, note: this.note, context: this.context }); } } } ``` -------------------------------- ### Common Context Variables Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Variables available in both Scripts and Hooks contexts for interacting with the note, content, context, and Obsidian app. ```APIDOC ## Common Context Variables These variables are available in both Scripts and Hooks contexts: ### `note` Functions for working with the current note's metadata. - `setTitle(title: string)`: Sets the title of the note. - `getTitle()`: Returns the note title. - `setTargetFolder(folder: string)`: Sets the target folder. - `getTargetFolder()`: Returns the target folder path. ### `content` Functions for manipulating the note's content. - `add(content: string)`: Adds content to the note. - `get()`: Gets the current content. - `modify(key: string, result: string)`: Replaces a substring with new content. - `addTag(tag: string)`: Adds a tag to frontmatter. - `addTags(tags: string[])`: Adds multiple tags to frontmatter. - `getTags()`: Gets all tags from frontmatter. - `addFrontMatter(frontmatter: Record)`: Adds properties to frontmatter. - `getFrontMatter()`: Gets all frontmatter properties. ### `context` A shared object for storing state between different execution steps. #### Request Example ```javascript // Store data in one step context.myData = { value: 42 }; // Use it in another step console.log(context.myData.value); // 42 ``` ### `app` The Obsidian API instance for advanced operations. ``` -------------------------------- ### List Links to the Current Day with Dataview Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/steps/markdown/Daily Note Template.md This Dataview snippet displays a table of notes that link to the current day. It shows the 'tags' and 'ctime' (creation time) of these linked notes, sorted by creation time in descending order. ```dataview TABLE tags, file.ctime WHERE dailyLink=[[{{today}}]] SORT file.ctime DESC ``` -------------------------------- ### Set Note Target Folder - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet illustrates how to specify a target folder for the current note using `note.setTargetFolder()`. This helps organize where the note will be saved or moved. ```javascript note.setTargetFolder("My Folder/Subfolder"); ``` -------------------------------- ### Add Multiple Tags to Note - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet demonstrates adding multiple tags to a note's frontmatter simultaneously using `content.addTags()`. It accepts an array of strings, each representing a tag. ```javascript content.addTags(["tag1", "tag2", "tag3"]); ``` -------------------------------- ### Store Data Between Script Steps - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet demonstrates using the `context` object to store and retrieve data across different script execution steps within a Zettelflow workflow. It's a simple key-value store. ```javascript context.myVariable = "Hello world!"; const storedValue = context.myVariable; ``` -------------------------------- ### Access Obsidian API - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet shows how to interact with the Obsidian API directly using the `app` variable. It provides access to core Obsidian functionalities like accessing the vault. ```javascript const allFiles = app.vault.getMarkdownFiles(); ``` -------------------------------- ### Add Frontmatter to Note - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet demonstrates adding custom properties to a note's frontmatter using `content.addFrontMatter()`. It takes a Record object where keys are property names and values are literals. ```javascript content.addFrontMatter({ key: "value", number: 123 }); ``` -------------------------------- ### Load and Manage Canvas Flows (TypeScript) Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt The FlowsImpl class manages canvas flows by parsing canvas files and providing navigation through the flow graph. It uses FileService and FrontmatterService for file operations and data parsing. The class supports adding, retrieving, updating, and deleting flows, with error handling for missing files or flows. ```typescript import { FlowsImpl, FlowImpl } from 'architecture/plugin/canvas'; import { FileService, FrontmatterService, YamlService } from 'architecture/plugin'; export class FlowsImpl implements Flows { private flows: Map; constructor() { this.flows = new Map(); } add = async (canvasPath: string) => { const canvasFile = await FileService.getFile(canvasPath); if (!canvasFile) { throw new Error(`Canvas file ${canvasPath} not found`); } const content = await FileService.getContent(canvasFile); const data: CanvasData = JSON.parse(content); const flow = new FlowImpl(data, canvasFile); this.flows.set(canvasFile.path, flow); return flow; } get = (id: string) => { const potentialFlow = this.flows.get(id); if (!potentialFlow) { throw new Error(`Flow ${id} not found`); } return potentialFlow; } // Refresh flow from disk if already cached update = async (canvasPath: string) => { const flow = this.flows.get(canvasPath); if (!flow) { return await this.add(canvasPath); } return flow; } delete = (id: string) => this.flows.delete(id); } ``` -------------------------------- ### Implement Custom Action Execute Method for Calendar Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt Defines the execution logic for the Calendar action, allowing it to add a date or date-time to a note's content, frontmatter, or context. It handles date formatting based on user-defined settings and supports different zones for insertion. ```typescript import { CustomZettelAction, ExecuteInfo } from 'architecture/api'; import { TypeService } from 'architecture/typing'; import moment from 'moment'; export class CalendarAction extends CustomZettelAction { id = "calendar"; defaultAction = { type: this.id, hasUI: true, id: this.id }; settings = calendarSettings; // Settings UI builder function settingsReader = calendarSettingsReader; // Read-only settings view link = "https://rafaelgb.github.io/Obsidian-ZettelFlow/actions/Calendar"; purpose = "Add a calendar (date/time) to your note."; async execute(info: ExecuteInfo) { const { content, element, context } = info; const { result, key, zone, staticBehaviour, staticValue, enableTime, format } = element; const valueToSave = staticBehaviour ? staticValue : result; if (TypeService.isString(key) && TypeService.isDate(valueToSave)) { const defaultFormat = enableTime ? "YYYY-MM-DDTHH:mm:ss" : "YYYY-MM-DD"; const formattedValue = moment(valueToSave).format(format || defaultFormat); switch (zone) { case "body": content.modify(key, formattedValue); break; case "context": context[key] = formattedValue; break; case "frontmatter": default: content.addFrontMatter({ [key]: formattedValue }); } } } getIcon() { return "calendar-days"; } getLabel() { return "Calendar"; } } ``` -------------------------------- ### Show Resolved Tasks in Last 30 Days with Dataview Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/steps/markdown/Daily Note Template.md This Dataview snippet lists tasks and epics that were resolved within the last 30 days. It displays the resolution date, deadline, purpose, and status, sorted by deadline. ```dataview TABLE wasResolved,deadline, purpose, status FROM #task | #epic WHERE status AND status="✅ resolved" AND wasResolved AND wasResolved >= date(today) - dur(30 days) SORT deadline ASC ``` -------------------------------- ### TypeScript: Execute Dynamic JavaScript Code Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt The ScriptAction allows users to define and execute custom JavaScript code during note creation or editing. It leverages the ZettelFlow API (zf) for accessing note content, context, and other functionalities. The code is executed within an async function for flexibility. ```typescript export class ScriptAction extends CustomZettelAction { id = "script"; purpose = "Run a JS script when the note is created/edited."; async execute(info: ExecuteInfo) { try { const element = info.element; const { content, note, context } = info; const { code } = element; // Create async function with ZettelFlow API access const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor; const fnBody = `return (async () => { ${code} })(content, note, context, zf); `; const functions = await fnsManager.getFns(); const scriptFn = new AsyncFunction( "element", "content", "note", "context", "zf", fnBody ); await scriptFn(element, content, note, context, functions); } catch (error) { log.error(`Error executing script: ${error}`); } } getIcon() { return "code-glyph"; } getLabel() { return "Script"; } } ``` -------------------------------- ### Internal API - Vault Operations Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Functions for interacting with the Obsidian vault, including resolving folders and obtaining files. ```APIDOC ## Internal API - Vault Operations Functions for working with the Obsidian vault, files, and folders. ### `resolveTFolder(path: string): TFolder` Resolves a path to a folder object. #### Parameters - **path** (string) - Required - The string path to the folder. #### Returns - TFolder - The resolved folder object. #### Throws - Error if the path doesn't resolve to a folder. ### `obtainFilesFrom(folder: TFolder, extensions: string[]): TFile[]` Gets files from a folder with optional extension filtering. #### Parameters - **folder** (TFolder) - Required - The TFolder object to search within. - **extensions** (string[]) - Optional - An array of file extensions to filter by. Defaults to `["md", "canvas"]`. #### Returns - TFile[] - An array of TFile objects sorted alphabetically by basename. ### Request Example (resolveTFolder) ```javascript // Get a folder reference const folder = zf.internal.vault.resolveTFolder("MyNotes/Projects"); ``` ### Request Example (obtainFilesFrom) ```javascript // Get all markdown files from a folder const folder = zf.internal.vault.resolveTFolder("MyNotes/Projects"); const mdFiles = zf.internal.vault.obtainFilesFrom(folder, ["md"]); ``` ``` -------------------------------- ### List Inbox Tasks with Dataview Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/steps/markdown/Daily Note Template.md This Dataview snippet retrieves tasks and epics from Obsidian notes that are not resolved or in progress. It displays their deadline, purpose, and status, sorted by deadline in ascending order. ```dataview TABLE deadline, purpose, status FROM #task | #epic WHERE status AND status!="✅ resolved" AND status!="🧐 On progress" SORT deadline ASC ``` -------------------------------- ### Internal API - User Scripts Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Access user-defined scripts loaded from the JS library folder. ```APIDOC ## Internal API - User Scripts User-defined scripts that are loaded from the JS library folder. Each script is available as a function under the `zf.internal.user` namespace, with the script filename (without extension) as the function name. ### Request Example ```javascript // Call a user script named "formatDate.js" const formattedDate = zf.internal.user.formatDate(new Date()); ``` ``` -------------------------------- ### Define Static Key-Value Options with JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/DynamicSelector.md This JavaScript code snippet provides a fixed set of key-value pairs for dynamic selector options. It returns a pre-defined array of string tuples, suitable for scenarios where options do not need to be generated dynamically based on external data. The format must be an array of [string, string] arrays. ```javascript return [ ["home", "Home"], ["about", "About Us"], ["contact", "Contact"], ]; ``` -------------------------------- ### Add Tag to Note - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet illustrates how to add a single tag to a note's frontmatter using `content.addTag()`. Tags are essential for organizing and categorizing notes in Obsidian. ```javascript content.addTag("mytag"); ``` -------------------------------- ### TypeScript: Add Backlinks with Post-Processing Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt The BackLinkAction extends CustomZettelAction to add backlinks to a note after it's created. It uses FileService and EditService to find and modify target files, inserting wikilinks based on configured patterns. This action is intended for background processing. ```typescript import { CustomZettelAction, ExecuteInfo } from 'architecture/api'; import { EditService, FileService } from 'architecture/plugin'; import { TFile } from 'obsidian'; export class BackLinkAction extends CustomZettelAction { id = "backlink"; purpose = "Add a backlink of the in-building note to another note."; async postProcess(info: ExecuteInfo, file: TFile) { const { element } = info; const { defaultFile, insertPattern = "{{wikilink}}", defaultHeading } = element; if (!defaultFile || !defaultHeading) { log.error(`Missing target file or heading for ${file.basename}`); return; } const mdLink = `\n${insertPattern.replace("{{wikilink}}", `[[${file.basename}]]`)}\n`; const targetFile = await FileService.getFile(defaultFile); if (targetFile) { await EditService.instance(targetFile) .setContent(await FileService.getContent(targetFile)) .insertBacklink(mdLink, defaultHeading) .save(); } } getIcon() { return "links-coming-in"; } getLabel() { return "Backlinks"; } isBackground() { return true; } // Indicates post-processing action } ``` -------------------------------- ### Call User Script from ZettelFlow Internal API (JavaScript) Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Executes a user-defined script loaded from the JS library folder within ZettelFlow. Scripts are accessible as functions under the `zf.internal.user` namespace, using the script's filename (without extension) as the function name. This allows for modular and reusable script functionality. ```javascript // Example: Call a user script named "formatDate.js" const formattedDate = zf.internal.user.formatDate(new Date()); ``` -------------------------------- ### Add Content to Note - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet demonstrates how to add new content to a note using the `content.add()` function. It's a fundamental operation for modifying note content within Zettelflow scripts. ```javascript content.add("Hello world!"); ``` -------------------------------- ### Obtain Files from Folder with Extension Filtering (JavaScript) Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Retrieves files from a specified TFolder, with an option to filter by file extensions. This function is part of the ZettelFlow internal vault operations. It accepts a TFolder object and an optional array of extensions, returning an array of TFile objects sorted alphabetically. ```javascript const folder = zf.internal.vault.resolveTFolder("MyNotes/Projects"); const mdFiles = zf.internal.vault.obtainFilesFrom(folder, ["md"]); ``` -------------------------------- ### TypeScript Hook Script for Event Response Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt This TypeScript script demonstrates how a hook can intercept property changes in ZettelFlow, modify frontmatter, remove properties, and trigger additional workflows based on specific conditions like a 'completed' status. ```typescript // Hook script triggered on property change const { request, response, file } = event; if (request.property === "status" && request.newValue === "completed") { // Add completion date response.frontmatter.completedDate = moment().format("YYYY-MM-DD"); // Calculate duration if startDate exists const startDate = request.frontmatter.startDate; if (startDate) { const duration = moment().diff(moment(startDate), 'days'); response.frontmatter.duration = `${duration} days`; } // Remove temporary properties response.removeProperties = ["tempNotes", "draft"]; // Trigger completion flow response.flowToTrigger = "completion-workflow"; } // Hook must return the event return event; ``` -------------------------------- ### Register ZettelFlow Actions in Obsidian Plugin Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt Registers all available custom actions with the ActionsStore during the plugin's onload event. This makes the actions available for use within the ZettelFlow plugin. It ensures that actions are properly initialized and cleaned up. ```typescript import { Plugin } from 'obsidian'; import { actionsStore } from 'architecture/api/store/ActionsStore'; import { PromptAction, NumberAction, CheckboxAction, SelectorAction, DynamicSelectorAction, CalendarAction, BackLinkAction, TagsAction, CssClassesAction, ScriptAction, TaskManagementAction } from 'actions'; export default class ZettelFlow extends Plugin { async onload() { await this.loadSettings(); // Register all available actions actionsStore.registerAction(new PromptAction()); actionsStore.registerAction(new NumberAction()); actionsStore.registerAction(new CheckboxAction()); actionsStore.registerAction(new SelectorAction()); actionsStore.registerAction(new DynamicSelectorAction()); actionsStore.registerAction(new CalendarAction()); actionsStore.registerAction(new BackLinkAction()); actionsStore.registerAction(new TagsAction()); actionsStore.registerAction(new CssClassesAction()); actionsStore.registerAction(new ScriptAction()); actionsStore.registerAction(new TaskManagementAction()); } onunload() { // Clean up registered actions actionsStore.unregisterAll(); } } ``` -------------------------------- ### Modify Note Content - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet shows how to substitute a specific substring within the note's content using the `content.modify()` function. It requires a key (the substring to find) and the result (the replacement string). ```javascript content.modify("old text", "new text"); ``` -------------------------------- ### Register Property Change Hooks in Obsidian with TypeScript Source: https://context7.com/rafaelgb/obsidian-zettelflow/llms.txt This TypeScript code demonstrates how to register a hook for 'changed' events on the Obsidian metadata cache. It debounces metadata updates to avoid performance issues and processes changes by comparing old and new frontmatter properties, executing custom scripts defined in settings. ```typescript import { fnsManager } from 'architecture/api'; import { FrontmatterService, VaultStateManager } from 'architecture/plugin'; export class VaultHooks { constructor(private plugin: ZettelFlow) { this.plugin.app.workspace.onLayoutReady(() => { plugin.registerEvent( this.plugin.app.metadataCache.on("changed", this.onCacheUpdate, this) ); }); } private onCacheUpdate = (file: TFile, _data: string, cache: CachedMetadata) => { if (VaultStateManager.INSTANCE.isFreezed() || file.extension !== "md") { return; } // Debounced processing const previous = this.debounceTimers.get(file.path); if (previous) window.clearTimeout(previous); const handle = window.setTimeout( () => this.processMetadataChange(file, cache).catch((e) => { log.error("[VaultHooks] Error processing metadata change:", e); }), 60 // milliseconds ); this.debounceTimers.set(file.path, handle); } private async processMetadataChange(file: TFile, cache: CachedMetadata) { const hooksCfg = this.plugin.settings.hooks || { properties: {}, folderFlowPath: "" }; const hooksEntries = Object.entries(hooksCfg.properties || {}); if (!hooksEntries.length) return; const fmPrev = this.getOrCreateFrontmatterService(file); const oldFrontmatter = fmPrev.getFrontmatter() ?? {}; const newFrontmatter = cache.frontmatter || {}; const dynamicFrontmatter = {}; let event = { file, request: { oldValue: "", newValue: "", property: "", frontmatter: newFrontmatter }, response: { frontmatter: dynamicFrontmatter, removeProperties: [] } }; VaultStateManager.INSTANCE.processStart(file.path); try { for (const [property, hookSettings] of hooksEntries) { const oldValue = oldFrontmatter[property]; const newValue = newFrontmatter[property]; if (!valuesEqual(oldValue, newValue)) { event.request = { oldValue, newValue, property, frontmatter: newFrontmatter }; event = await this.executeHook(hookSettings.script, event); } } if (hasFrontmatterMutations(event.response.frontmatter, event.response.removeProperties)) { await fmPrev.setProperties( event.response.frontmatter, event.response.removeProperties ); VaultStateManager.INSTANCE.update(file); } } finally { VaultStateManager.INSTANCE.processFinished(file.path); } } private async executeHook(script: string, event: HookEvent): Promise { const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor; const fnBody = `return (async () => { ${script} return event; })(event, zf);`; const functions = await fnsManager.getFns(); const scriptFn = new AsyncFunction("event", "zf", fnBody); return await scriptFn(event, functions); } } ``` -------------------------------- ### Set Note Title - JavaScript Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/actions/Script.md This snippet shows how to change the title of the current note using the `note.setTitle()` function. This is useful for dynamically renaming notes within a workflow. ```javascript note.setTitle("New Note Title"); ``` -------------------------------- ### Resolve TFolder using ZettelFlow Internal API (JavaScript) Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/api/ZettelFlowAPI.md Resolves a given string path to a TFolder object within the Obsidian vault. This function is part of the ZettelFlow internal vault operations and requires a valid folder path. It returns a TFolder object or throws an error if the path is invalid. ```javascript const folder = zf.internal.vault.resolveTFolder("MyNotes/Projects"); ``` -------------------------------- ### Update Frontmatter in ZettelFlow Property Hook Source: https://github.com/rafaelgb/obsidian-zettelflow/blob/main/docs/vault-hooks/property-hooks/configuration.md This JavaScript snippet demonstrates how to update frontmatter properties within a ZettelFlow property hook. It checks if the 'progress' property has reached 100 and, if so, sets the 'status' property to 'Complete'. The hook must always return the event object for ZettelFlow to process changes. ```javascript // Example: Update a 'status' property when 'progress' reaches 100 if (event.request.property === 'progress' && event.request.newValue === 100) { event.response.frontmatter.status = 'Complete'; } // Always return the event object return event; ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.