feat: add spark table completion and rolling support
Implement "spark tables" functionality, which allows users to roll dice against markdown tables to retrieve specific values. - Add `sparkTablesSource` to scan markdown files for tables starting with a dice notation (e.g., d6, d20). - Implement `/spark` command in the journal to resolve and roll spark tables. - Add a new message type `spark` with a dedicated UI component to render the results. - Update completions API to include spark table metadata.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
import { createSignal } from "solid-js";
|
||||
import Slugger from "github-slugger";
|
||||
import { extractHeadings } from "../../data-loader/toc";
|
||||
import {
|
||||
getPathsByExtension,
|
||||
@@ -29,9 +30,18 @@ export interface LinkCompletion {
|
||||
section: string | null;
|
||||
}
|
||||
|
||||
export interface SparkTableCompletion {
|
||||
label: string;
|
||||
notation: string;
|
||||
slug: string;
|
||||
filePath: string;
|
||||
headers: string[];
|
||||
}
|
||||
|
||||
export interface JournalCompletions {
|
||||
dice: DiceCompletion[];
|
||||
links: LinkCompletion[];
|
||||
sparkTables: SparkTableCompletion[];
|
||||
}
|
||||
|
||||
export type CompletionsState =
|
||||
@@ -56,6 +66,7 @@ async function tryServer(): Promise<JournalCompletions | null> {
|
||||
return {
|
||||
dice: Array.isArray(data.dice) ? data.dice : [],
|
||||
links: Array.isArray(data.links) ? data.links : [],
|
||||
sparkTables: Array.isArray(data.sparkTables) ? data.sparkTables : [],
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
@@ -68,7 +79,9 @@ async function scanClientSide(): Promise<JournalCompletions> {
|
||||
const paths = await getPathsByExtension("md");
|
||||
const dice: DiceCompletion[] = [];
|
||||
const links: LinkCompletion[] = [];
|
||||
const sparkTables: SparkTableCompletion[] = [];
|
||||
const tagRegex = /<md-dice[^>]*>\s*([\s\S]*?)\s*<\/md-dice>/gi;
|
||||
const slugger = new Slugger();
|
||||
|
||||
for (const filePath of paths) {
|
||||
const content = await getIndexedData(filePath);
|
||||
@@ -95,9 +108,52 @@ async function scanClientSide(): Promise<JournalCompletions> {
|
||||
section: heading.id ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
// Spark table scan
|
||||
const sparkLines = content.split(/\r?\n/);
|
||||
for (let i = 0; i < sparkLines.length; i++) {
|
||||
const headerCells = splitTableRow(sparkLines[i]);
|
||||
if (!headerCells || headerCells.length < 2) continue;
|
||||
if (!/^d\d+$/i.test(headerCells[0])) continue;
|
||||
|
||||
if (i + 1 >= sparkLines.length) continue;
|
||||
const sepCells = splitTableRow(sparkLines[i + 1]);
|
||||
if (!sepCells || !sepCells.every((c) => /^:?-{3,}:?$/.test(c))) continue;
|
||||
|
||||
let j = i + 2;
|
||||
while (j < sparkLines.length && splitTableRow(sparkLines[j])) j++;
|
||||
if (j <= i + 2) continue;
|
||||
|
||||
const dataHeaders = headerCells.slice(1);
|
||||
const stSlug = dataHeaders
|
||||
.map((h) => slugger.slug(h.toLowerCase()))
|
||||
.join("-");
|
||||
|
||||
// Combined key: pageName-columnSlug
|
||||
const combinedSlug = `${fileName}-${stSlug}`;
|
||||
|
||||
sparkTables.push({
|
||||
label: `${fileName} § ${stSlug}`,
|
||||
notation: headerCells[0],
|
||||
slug: combinedSlug,
|
||||
filePath: basePath,
|
||||
headers: dataHeaders,
|
||||
});
|
||||
|
||||
i = j - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return { dice, links };
|
||||
return { dice, links, sparkTables };
|
||||
}
|
||||
|
||||
function splitTableRow(line: string): string[] | null {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed.includes("|")) return null;
|
||||
let inner = trimmed;
|
||||
if (inner.startsWith("|")) inner = inner.slice(1);
|
||||
if (inner.endsWith("|")) inner = inner.slice(0, -1);
|
||||
return inner.split("|").map((c) => c.trim());
|
||||
}
|
||||
|
||||
// ------------------- Init (runs eagerly at import time) -------------------
|
||||
@@ -157,5 +213,5 @@ export function useJournalCompletions(): {
|
||||
if (s.status === "loaded") {
|
||||
return { state: s, data: s.data };
|
||||
}
|
||||
return { state: s, data: { dice: [], links: [] } };
|
||||
return { state: s, data: { dice: [], links: [], sparkTables: [] } };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user