Add Dataview and Bases generators (Toolbox Phase 2)
Two more inside-LMS generators under /tools, completing the 6-tool set: - Dataview: DQL builder (TABLE/LIST/TASK/CALENDAR, FROM/WHERE/SORT/LIMIT), output wrapped in a dataview code block - Bases: .base YAML builder (views, filters and:, order, sort, groupBy, limit); filter expressions emitted as single-quoted YAML scalars for safety Syntax verified against official Obsidian docs (research workflow). Audit fix: Bases sort entries use 'column:' (not 'property:') per documented working .base files; 'property:' is kept only for groupBy. 14 new tests (47 total). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
export type DataviewQueryType = "TABLE" | "LIST" | "TASK" | "CALENDAR";
|
||||
|
||||
export interface DataviewColumn {
|
||||
/** выражение/поле, напр. file.name, status, file.mtime */
|
||||
expr: string;
|
||||
/** опц. человекочитаемый заголовок колонки (AS "…") */
|
||||
alias?: string;
|
||||
}
|
||||
|
||||
export interface DataviewInput {
|
||||
queryType: DataviewQueryType;
|
||||
/** WITHOUT ID — скрыть первую колонку-ссылку (TABLE/LIST) */
|
||||
withoutId?: boolean;
|
||||
/** колонки/поля (TABLE — все; LIST/CALENDAR — берётся первое) */
|
||||
columns?: DataviewColumn[];
|
||||
/** источник FROM как есть: '#tag', '"Папка"', '[[Заметка]]', их комбинации */
|
||||
from?: string;
|
||||
/** условие WHERE как есть */
|
||||
where?: string;
|
||||
/** поле сортировки */
|
||||
sortField?: string;
|
||||
sortDir?: "ASC" | "DESC";
|
||||
/** LIMIT n */
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
function renderColumns(cols: DataviewColumn[]): string {
|
||||
return cols
|
||||
.filter((c) => c.expr.trim())
|
||||
.map((c) => {
|
||||
const expr = c.expr.trim();
|
||||
const alias = c.alias?.trim();
|
||||
return alias ? `${expr} AS "${alias.replace(/"/g, '\\"')}"` : expr;
|
||||
})
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
/** Собирает запрос Dataview DQL и оборачивает в кодблок dataview. */
|
||||
export function buildDataview(input: DataviewInput): string {
|
||||
const lines: string[] = [];
|
||||
const cols = input.columns ?? [];
|
||||
const withoutId = input.withoutId ? " WITHOUT ID" : "";
|
||||
|
||||
switch (input.queryType) {
|
||||
case "TABLE": {
|
||||
const c = renderColumns(cols);
|
||||
lines.push(`TABLE${withoutId}${c ? " " + c : ""}`);
|
||||
break;
|
||||
}
|
||||
case "LIST": {
|
||||
const first = cols.find((c) => c.expr.trim())?.expr.trim();
|
||||
lines.push(`LIST${withoutId}${first ? " " + first : ""}`);
|
||||
break;
|
||||
}
|
||||
case "TASK":
|
||||
lines.push("TASK");
|
||||
break;
|
||||
case "CALENDAR": {
|
||||
const dateField = cols.find((c) => c.expr.trim())?.expr.trim() || "file.ctime";
|
||||
lines.push(`CALENDAR ${dateField}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (input.from?.trim()) lines.push(`FROM ${input.from.trim()}`);
|
||||
if (input.where?.trim()) lines.push(`WHERE ${input.where.trim()}`);
|
||||
if (input.sortField?.trim()) lines.push(`SORT ${input.sortField.trim()} ${input.sortDir ?? "DESC"}`);
|
||||
if (input.limit && input.limit > 0) lines.push(`LIMIT ${Math.floor(input.limit)}`);
|
||||
|
||||
return "```dataview\n" + lines.join("\n") + "\n```";
|
||||
}
|
||||
Reference in New Issue
Block a user