vault commit: 2025-10-08 22:44, 16 files changed

This commit is contained in:
Jason McPherson
2025-10-08 22:44:44 -05:00
parent 21840a322c
commit ec9e34e8f6
16 changed files with 633 additions and 24 deletions
+101
View File
@@ -0,0 +1,101 @@
<%*
/* PF2e AI Session Summary — FULL NOTE MODE (single-string call)
- Uses the whole note body (not just bullets)
- Strips frontmatter, code fences, and prior summaries/history
- Lets you pick Short / Standard / Detailed length
*/
const file = app.workspace.getActiveFile();
if (!file) { new Notice("Open a session note first."); return; }
/* ---------- Read & sanitize whole note ---------- */
let body = await app.vault.read(file);
// strip YAML frontmatter
body = body.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
// strip prior summaries & history blocks if present
body = body.replace(/<!--\s*SUMMARY-START\s*-->[\s\S]*?<!--\s*SUMMARY-END\s*-->/gi, "");
body = body.replace(/<!--\s*SUMMARY-HISTORY-START\s*-->[\s\S]*?<!--\s*SUMMARY-HISTORY-END\s*-->/gi, "");
// strip code fences & inline dataview
body = body.replace(/```[\s\S]*?```/g, "");
body = body.replace(/`=\s*[^`]+`/g, "");
// normalize whitespace
body = body.replace(/\r/g, "").trim();
// If the note is very long, cap input (you can raise this safely)
const MAX_INPUT_CHARS = 40000;
if (body.length > MAX_INPUT_CHARS) body = body.slice(0, MAX_INPUT_CHARS);
/* ---------- Metadata ---------- */
const cache = app.metadataCache.getFileCache(file) || {};
const fm = cache.frontmatter || {};
const seshName = fm.Session_Name || tp.file.title || file.basename;
const seshDate = fm.Date || tp.date.now("YYYY-MM-DD");
/* ---------- Length selector ---------- */
const lenPick = await tp.system.suggester(
["Short (~6 bullets)","Standard (~12 bullets)","Detailed (~20 bullets)"],
["short","standard","detailed"]
) ?? "standard";
const bulletTarget = (lenPick === "short") ? 6 : (lenPick === "detailed") ? 20 : 12;
/* ---------- Build single-string prompt ---------- */
const prompt = `You are a concise Pathfinder 2e GM assistant. Summarize the FULL session note below.
Return ONLY:
**One-line recap (bold)**
- Key beats (bulleted, chronological, ${bulletTarget} bullets max)
- NPCs & factions touched (preserve existing [[wikilinks]] verbatim if present)
- Encounters/checks/outcomes (brief)
- Loot/rewards/XP (brief)
- Next hooks/to-dos (bullets)
DO NOT echo raw headings, “Highlight::”, or placeholder text. Cover ALL major content, not just bullet lists.
Session: "${seshName}" on ${seshDate}
=== NOTE START ===
${body}
=== NOTE END ===`;
/* ---------- Call AI (string-only) ---------- */
let summary = "";
try {
const resp = await tp.ai.chat(prompt);
summary = (typeof resp === "string") ? resp : (resp?.content ?? resp?.text ?? "");
} catch (e) {
console.error("AI error:", e);
}
/* ---------- Fallback if model returns empty ---------- */
if (!summary || !summary.trim()) {
// Build a tidy, non-noisy fallback from the whole note (no headings/labels)
const lines = body
.split("\n")
.map(s => s.trim())
.filter(s => s && !/^#{1,6}\s/.test(s)) // no markdown headings
.filter(s => !/^\*\*[^*]+:\*\*$/.test(s)) // no bold-only section labels
.filter(s => !/^Highlight::/i.test(s)) // no "Highlight::"
.filter(s => !/^Round\s+\d+/i.test(s)) // no "Round X"
.filter(s => s !== "[[ ]]"); // no empty wikilinks
const oneLine = `**${seshName} — ${seshDate}:** ${lines[0] || "Session recap unavailable."}`;
const bullets = lines.slice(1, 1 + Math.min(20, bulletTarget + 4)).map(s => `- ${s}`);
summary = [oneLine, ...bullets].join("\n");
}
/* ---------- Inject between SUMMARY markers ---------- */
const START = "<!-- SUMMARY-START -->";
const END = "<!-- SUMMARY-END -->";
let content = await app.vault.read(file);
const sIdx = content.indexOf(START), eIdx = content.indexOf(END);
if (sIdx === -1 || eIdx === -1 || eIdx < sIdx) { new Notice("Could not find SUMMARY markers."); return; }
const before = content.slice(0, sIdx + START.length);
const after = content.slice(eIdx);
await app.vault.modify(file, before + "\n" + summary.trim() + "\n" + after);
new Notice("AI session summary generated.");
%>
+108
View File
@@ -0,0 +1,108 @@
---
Date: <% tp.date.now("YYYY-MM-DD") %>
Session_Name: <% tp.file.title %>
Tags: [pf2e, session]
---
# <% tp.file.title %>
> **Date:** `= this.Date` • **Name:** `= this.Session_Name`
---
## 📄 Session Summary (auto)
<!-- SUMMARY-START -->
*No summary yet — click “Generate Summary” below.*
<!-- SUMMARY-END -->
```button
name ✍️ Generate AI Summary
type template
action Templates/Generate AI Session Summary.md
templater true
```
## 🧭 Where / When
- **Region / Location:**
- **In-game Date / Time:**
- **Weather / Conditions:**
---
## 👥 PCs Present / Absent
- **Present:**
- **Absent:**
---
## ⭐ Highlights (feed the auto-summary)
> Add one or more `Highlight::` lines. The button will build a summary from these.
- Highlight::
- Highlight::
- Highlight::
---
## 🎯 Quests & Progress
- **Active Objectives:**
- **Progress / Resolutions:**
---
## 🗣️ NPCs Met / Factions
- **NPCs:** [[ ]] [[ ]] [[ ]]
- **Factions Touched:** [[ ]] [[ ]]
---
## ⚔️ Encounters
- **Social / Exploration:**
- **Combat:**
- **Outcome / Loot:**
---
## 🧠 Intel Captured
---
## 📦 Treasure & Rewards
- **Items:**
- **Coin:**
- **XP / Milestones:**
---
## 🔜 Next Hooks / To-Dos
- [ ]
- [ ]
- [ ]
+9
View File
@@ -0,0 +1,9 @@
<%*
try {
const out = await tp.ai.chat("Say 'OK' if you can read this.");
new Notice("AI replied: " + (typeof out === "string" ? out.slice(0,60) : JSON.stringify(out).slice(0,60)));
} catch (e) {
console.error(e);
new Notice("AI call threw an error (check console).");
}
%>