sensemaking 0.13.2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli/shared.js +4 -2
- package/dist/cjs/cli/shared.js.map +1 -1
- package/dist/cjs/column-hint.d.cts +2 -0
- package/dist/cjs/column-hint.d.ts +2 -0
- package/dist/cjs/column-hint.js +104 -0
- package/dist/cjs/column-hint.js.map +1 -0
- package/dist/cjs/commands/scope.js +7 -1
- package/dist/cjs/commands/scope.js.map +1 -1
- package/dist/cjs/db/open.d.cts +1 -1
- package/dist/cjs/db/open.d.ts +1 -1
- package/dist/cjs/db/open.js +1 -1
- package/dist/cjs/db/open.js.map +1 -1
- package/dist/cjs/features/embed.js +4 -6
- package/dist/cjs/features/embed.js.map +1 -1
- package/dist/cjs/features/links.js +4 -2
- package/dist/cjs/features/links.js.map +1 -1
- package/dist/cjs/features/sections.js +4 -6
- package/dist/cjs/features/sections.js.map +1 -1
- package/dist/cjs/features/tags.js +172 -9
- package/dist/cjs/features/tags.js.map +1 -1
- package/dist/cjs/fences.d.cts +5 -0
- package/dist/cjs/fences.d.ts +5 -0
- package/dist/cjs/fences.js +54 -0
- package/dist/cjs/fences.js.map +1 -0
- package/dist/esm/cli/shared.js +4 -2
- package/dist/esm/cli/shared.js.map +1 -1
- package/dist/esm/column-hint.d.ts +2 -0
- package/dist/esm/column-hint.js +32 -0
- package/dist/esm/column-hint.js.map +1 -0
- package/dist/esm/commands/scope.js +7 -1
- package/dist/esm/commands/scope.js.map +1 -1
- package/dist/esm/db/open.d.ts +1 -1
- package/dist/esm/db/open.js +1 -1
- package/dist/esm/db/open.js.map +1 -1
- package/dist/esm/features/embed.js +4 -6
- package/dist/esm/features/embed.js.map +1 -1
- package/dist/esm/features/links.js +4 -2
- package/dist/esm/features/links.js.map +1 -1
- package/dist/esm/features/sections.js +4 -6
- package/dist/esm/features/sections.js.map +1 -1
- package/dist/esm/features/tags.js +167 -8
- package/dist/esm/features/tags.js.map +1 -1
- package/dist/esm/fences.d.ts +5 -0
- package/dist/esm/fences.js +43 -0
- package/dist/esm/fences.js.map +1 -0
- package/package.json +1 -1
- package/skills/sense/SKILL.md +2 -1
- package/skills/sense-setup/SKILL.md +1 -1
|
@@ -66,10 +66,12 @@ function extract(_raw, body) {
|
|
|
66
66
|
};
|
|
67
67
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
68
68
|
try {
|
|
69
|
-
|
|
69
|
+
// To the first ]], as Obsidian parses it, so a heading or alias holding a lone ] still
|
|
70
|
+
// matches; anchor and alias split off in code, since a class-based regex stops at the ].
|
|
71
|
+
for(var _iterator = body.matchAll(/\[\[(.*?)\]\]/g)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
70
72
|
var m = _step.value;
|
|
71
73
|
var _m_index;
|
|
72
|
-
var target = m[1].trim();
|
|
74
|
+
var target = m[1].split('|')[0].split('#')[0].trim();
|
|
73
75
|
if (target) add(target, body[((_m_index = m.index) !== null && _m_index !== void 0 ? _m_index : 0) - 1] === '!');
|
|
74
76
|
}
|
|
75
77
|
} catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/links.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { FileStat } from '../scan.ts';\nimport type { Feature, ReconcileDelta } from './types.ts';\n\n// links(src, target, target_base, dst, embed): target as written, target_base its baseKey\n// (indexed, for the incremental resolve below), dst the resolved path or NULL (a\n// queryable dead link), embed whether it's `![[x]]`/`` rather than `[[x]]`/`[](x.md)`\n// -- Obsidian's grain: a target both linked and embedded in the same note is two rows.\n\n// Wikilinks ([[target]], [[target#anchor|alias]], embeds) plus relative markdown links to .md files.\nfunction extract(_raw: string, body: string): Array<{ target: string; embed: boolean }> {\n const seen = new Set<string>();\n const results: Array<{ target: string; embed: boolean }> = [];\n const add = (target: string, embed: boolean) => {\n const key = `${target}\\0${embed ? '1' : '0'}`;\n if (seen.has(key)) return;\n seen.add(key);\n results.push({ target, embed });\n };\n for (const m of body.matchAll(/\\[\\[([^\\]|#]+)(?:#[^\\]|]*)?(?:\\|[^\\]]*)?\\]\\]/g)) {\n const target = m[1].trim();\n if (target) add(target, body[(m.index ?? 0) - 1] === '!');\n }\n for (const m of body.matchAll(/(!)?\\[[^\\]]*\\]\\(([^)]+\\.md)(?:#[^)]*)?\\)/g)) {\n const target = m[2].trim();\n if (target && !/^[a-z]+:\\/\\//i.test(target)) add(target, m[1] === '!');\n }\n return results;\n}\n\nfunction cleanTarget(target: string): string {\n return target.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\nfunction baseKey(path: string): string {\n return posix.basename(path).replace(/\\.md$/i, '').toLowerCase();\n}\n\n// Obsidian-style: exact relative path (with/without .md), path relative to the linking\n// note's directory, then basename match (lexicographically first on ties).\nfunction resolveTarget(src: string, target: string, pathSet: Set<string>, byBase: Map<string, string[]>): string | null {\n const clean = cleanTarget(target);\n const fromSrc = posix.normalize(posix.join(posix.dirname(src), clean));\n for (const candidate of [clean, `${clean}.md`, fromSrc, `${fromSrc}.md`]) {\n if (pathSet.has(candidate)) return candidate;\n }\n return byBase.get(baseKey(clean))?.[0] ?? null;\n}\n\nfunction buildByBase(files: FileStat[]): Map<string, string[]> {\n const byBase = new Map<string, string[]>();\n for (const path of files.map((f) => f.relPath).sort()) {\n const key = baseKey(path);\n const list = byBase.get(key);\n if (list) list.push(path);\n else byBase.set(key, [path]);\n }\n return byBase;\n}\n\ninterface LinkRow {\n src: string;\n target: string;\n dst: string | null;\n embed: number;\n}\n\n// Applies resolveTarget to `rows`, writing only rows whose dst actually changes. The UPDATE\n// keys on (src, target): a link/embed sibling pair shares one resolution, so either row's\n// mismatch rewrites both.\nfunction resolveRows(db: DatabaseSync, rows: LinkRow[], pathSet: Set<string>, byBase: Map<string, string[]>): boolean {\n const update = db.prepare('UPDATE links SET dst = ? WHERE src = ? AND target = ?');\n let changed = false;\n for (const row of rows) {\n const dst = resolveTarget(row.src, row.target, pathSet, byBase);\n if (dst !== row.dst) {\n update.run(dst, row.src, row.target);\n changed = true;\n }\n }\n return changed;\n}\n\n// A new or deleted file can change any note's resolution, so re-resolve the whole table.\n// Fallback for cold builds and large deltas -- see afterReconcile's threshold.\nfunction resolveAll(db: DatabaseSync, files: FileStat[]): boolean {\n const pathSet = new Set(files.map((f) => f.relPath));\n const byBase = buildByBase(files);\n const rows = db.prepare('SELECT src, target, dst, embed FROM links').all() as unknown as LinkRow[];\n return resolveRows(db, rows, pathSet, byBase);\n}\n\n// Re-resolve only what this reconcile could have touched: re-stored sources, links whose\n// target basename matches an added/vanished file, and links whose dst just vanished. All\n// three are indexed lookups, so this never reads the whole table.\nfunction resolveIncremental(db: DatabaseSync, delta: ReconcileDelta): boolean {\n const pathSet = new Set(delta.files.map((f) => f.relPath));\n const byBase = buildByBase(delta.files);\n\n const changedBasenames = new Set<string>();\n for (const p of delta.added) changedBasenames.add(baseKey(p));\n for (const p of delta.vanished) changedBasenames.add(baseKey(p));\n\n // \\0 can't appear in a path or target, so the key never collides; a space can.\n const candidates = new Map<string, LinkRow>();\n const collect = (rows: LinkRow[]) => {\n for (const row of rows) candidates.set(`${row.src}\\0${row.target}\\0${row.embed}`, row);\n };\n // Chunked so a large-but-under-threshold delta never exceeds SQLite's bound-variable\n // limit (SQLITE_MAX_VARIABLE_NUMBER, 32766).\n const collectIn = (column: string, keys: string[]) => {\n for (let i = 0; i < keys.length; i += 500) {\n const chunk = keys.slice(i, i + 500);\n const placeholders = chunk.map(() => '?').join(', ');\n collect(db.prepare(`SELECT src, target, dst, embed FROM links WHERE ${column} IN (${placeholders})`).all(...chunk) as unknown as LinkRow[]);\n }\n };\n\n collectIn('src', delta.reparsed);\n collectIn('target_base', [...changedBasenames]);\n collectIn('dst', delta.vanished);\n\n return resolveRows(db, [...candidates.values()], pathSet, byBase);\n}\n\n// Resolved edges, for rank and for search's graph expansion.\nexport function linkEdges(db: DatabaseSync): [string, string][] {\n // One edge per row, as 0.12's grain always had it -- two written targets resolving to one\n // dst stay two edges (a weight the fusion evals were gated on). The only exclusion is an\n // embed whose exact (src, target) also exists as a link: that second row is new with the\n // embed grain and would double an edge that used to be one row. The NOT EXISTS probes the\n // primary key and fires only for embed rows; both branches still scan the table.\n const sql = `SELECT src, dst FROM links WHERE dst IS NOT NULL AND embed = 0\n UNION ALL\n SELECT src, dst FROM links l WHERE dst IS NOT NULL AND embed = 1\n AND NOT EXISTS (SELECT 1 FROM links l0 WHERE l0.src = l.src AND l0.target = l.target AND l0.embed = 0)`;\n return (db.prepare(sql).all() as Array<{ src: string; dst: string }>).map((r) => [r.src, r.dst]);\n}\n\n// remove() has already deleted the rows by the time afterReconcile runs, so it records here\n// whether they carried edges. Keyed on the delta object, so the state dies with the reconcile.\nconst removedWithLinks = new WeakMap<ReconcileDelta, Set<string>>();\n\nfunction recordRemoved(delta: ReconcileDelta, path: string): void {\n let set = removedWithLinks.get(delta);\n if (!set) {\n set = new Set();\n removedWithLinks.set(delta, set);\n }\n set.add(path);\n}\n\n// remove() runs per path; the vanished list is an array, so cache the Set per delta.\nconst vanishedSets = new WeakMap<ReconcileDelta, Set<string>>();\nfunction vanishedSet(delta: ReconcileDelta): Set<string> {\n let set = vanishedSets.get(delta);\n if (!set) {\n set = new Set(delta.vanished);\n vanishedSets.set(delta, set);\n }\n return set;\n}\n\nexport const links: Feature = {\n name: 'links',\n schema(db) {\n db.exec('CREATE TABLE IF NOT EXISTS links (src TEXT, target TEXT, target_base TEXT, dst TEXT, embed INTEGER, PRIMARY KEY (src, target, embed))');\n db.exec('CREATE INDEX IF NOT EXISTS links_dst ON links(dst)');\n db.exec('CREATE INDEX IF NOT EXISTS links_target_base ON links(target_base)');\n },\n extract,\n // Reparsed docs are diffed in store() below instead of wiped here: deleting and\n // re-inserting resets dst to NULL, which made every touch-only reparse read as an edge\n // change and recompute PageRank -- measured as most of the remaining update cost.\n remove(db, path, delta) {\n if (!vanishedSet(delta).has(path)) return;\n const result = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n if (Number(result.changes) > 0) recordRemoved(delta, path);\n },\n store(db, path, extracted, delta) {\n const targets = extracted as Array<{ target: string; embed: boolean }>;\n // Stale rows (target/embed pairs the new parse no longer contains) are real edge removals.\n // \\0 can't appear in a target, so the composite key never collides.\n let stale: ReturnType<ReturnType<DatabaseSync['prepare']>['run']>;\n if (targets.length === 0) {\n stale = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n } else {\n const placeholders = targets.map(() => '?').join(', ');\n const keys = targets.map((t) => `${t.target}\\0${t.embed ? '1' : '0'}`);\n stale = db.prepare(`DELETE FROM links WHERE src = ? AND (target || char(0) || embed) NOT IN (${placeholders})`).run(path, ...keys);\n }\n if (Number(stale.changes) > 0) recordRemoved(delta, path);\n // Upsert preserves dst on surviving rows, so an unchanged link resolves to the same\n // value and reports no change; only genuinely new rows start at NULL.\n const insert = db.prepare('INSERT INTO links (src, target, target_base, dst, embed) VALUES (?, ?, ?, NULL, ?) ON CONFLICT(src, target, embed) DO UPDATE SET target_base = excluded.target_base');\n for (const { target, embed } of targets) insert.run(path, target, baseKey(cleanTarget(target)), embed ? 1 : 0);\n },\n afterReconcile(db, delta) {\n // Any deleted rows that carried edges mean the edge set shrank -- whether the file\n // vanished or was reparsed down to fewer/no links. dstChanged alone misses the\n // reparsed-to-zero-links case (no surviving rows to re-resolve).\n const removeHadLinks = (removedWithLinks.get(delta)?.size ?? 0) > 0;\n\n const churn = delta.reparsed.length + delta.vanished.length;\n // Past this share of the tree, a full pass is the only one guaranteed to match\n // resolveTarget's ambiguity rules. A cold build clears the threshold on its own.\n const large = delta.files.length === 0 || churn > 0.2 * delta.files.length;\n\n const dstChanged = large ? resolveAll(db, delta.files) : resolveIncremental(db, delta);\n delta.linksChanged = dstChanged || removeHadLinks;\n },\n};\n"],"names":["linkEdges","links","extract","_raw","body","seen","Set","results","add","target","embed","key","has","push","matchAll","m","trim","index","test","cleanTarget","replace","baseKey","path","posix","basename","toLowerCase","resolveTarget","src","pathSet","byBase","clean","fromSrc","normalize","join","dirname","candidate","get","buildByBase","files","Map","map","f","relPath","sort","list","set","resolveRows","db","rows","update","prepare","changed","row","dst","run","resolveAll","all","resolveIncremental","delta","changedBasenames","added","p","vanished","candidates","collect","collectIn","column","keys","i","length","chunk","slice","placeholders","reparsed","values","sql","r","removedWithLinks","WeakMap","recordRemoved","vanishedSets","vanishedSet","name","schema","exec","remove","result","Number","changes","store","extracted","targets","stale","t","insert","afterReconcile","removeHadLinks","size","churn","large","dstChanged","linksChanged"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/links.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { FileStat } from '../scan.ts';\nimport type { Feature, ReconcileDelta } from './types.ts';\n\n// links(src, target, target_base, dst, embed): target as written, target_base its baseKey\n// (indexed, for the incremental resolve below), dst the resolved path or NULL (a\n// queryable dead link), embed whether it's `![[x]]`/`` rather than `[[x]]`/`[](x.md)`\n// -- Obsidian's grain: a target both linked and embedded in the same note is two rows.\n\n// Wikilinks ([[target]], [[target#anchor|alias]], embeds) plus relative markdown links to .md files.\nfunction extract(_raw: string, body: string): Array<{ target: string; embed: boolean }> {\n const seen = new Set<string>();\n const results: Array<{ target: string; embed: boolean }> = [];\n const add = (target: string, embed: boolean) => {\n const key = `${target}\\0${embed ? '1' : '0'}`;\n if (seen.has(key)) return;\n seen.add(key);\n results.push({ target, embed });\n };\n // To the first ]], as Obsidian parses it, so a heading or alias holding a lone ] still\n // matches; anchor and alias split off in code, since a class-based regex stops at the ].\n for (const m of body.matchAll(/\\[\\[(.*?)\\]\\]/g)) {\n const target = m[1].split('|')[0].split('#')[0].trim();\n if (target) add(target, body[(m.index ?? 0) - 1] === '!');\n }\n for (const m of body.matchAll(/(!)?\\[[^\\]]*\\]\\(([^)]+\\.md)(?:#[^)]*)?\\)/g)) {\n const target = m[2].trim();\n if (target && !/^[a-z]+:\\/\\//i.test(target)) add(target, m[1] === '!');\n }\n return results;\n}\n\nfunction cleanTarget(target: string): string {\n return target.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\nfunction baseKey(path: string): string {\n return posix.basename(path).replace(/\\.md$/i, '').toLowerCase();\n}\n\n// Obsidian-style: exact relative path (with/without .md), path relative to the linking\n// note's directory, then basename match (lexicographically first on ties).\nfunction resolveTarget(src: string, target: string, pathSet: Set<string>, byBase: Map<string, string[]>): string | null {\n const clean = cleanTarget(target);\n const fromSrc = posix.normalize(posix.join(posix.dirname(src), clean));\n for (const candidate of [clean, `${clean}.md`, fromSrc, `${fromSrc}.md`]) {\n if (pathSet.has(candidate)) return candidate;\n }\n return byBase.get(baseKey(clean))?.[0] ?? null;\n}\n\nfunction buildByBase(files: FileStat[]): Map<string, string[]> {\n const byBase = new Map<string, string[]>();\n for (const path of files.map((f) => f.relPath).sort()) {\n const key = baseKey(path);\n const list = byBase.get(key);\n if (list) list.push(path);\n else byBase.set(key, [path]);\n }\n return byBase;\n}\n\ninterface LinkRow {\n src: string;\n target: string;\n dst: string | null;\n embed: number;\n}\n\n// Applies resolveTarget to `rows`, writing only rows whose dst actually changes. The UPDATE\n// keys on (src, target): a link/embed sibling pair shares one resolution, so either row's\n// mismatch rewrites both.\nfunction resolveRows(db: DatabaseSync, rows: LinkRow[], pathSet: Set<string>, byBase: Map<string, string[]>): boolean {\n const update = db.prepare('UPDATE links SET dst = ? WHERE src = ? AND target = ?');\n let changed = false;\n for (const row of rows) {\n const dst = resolveTarget(row.src, row.target, pathSet, byBase);\n if (dst !== row.dst) {\n update.run(dst, row.src, row.target);\n changed = true;\n }\n }\n return changed;\n}\n\n// A new or deleted file can change any note's resolution, so re-resolve the whole table.\n// Fallback for cold builds and large deltas -- see afterReconcile's threshold.\nfunction resolveAll(db: DatabaseSync, files: FileStat[]): boolean {\n const pathSet = new Set(files.map((f) => f.relPath));\n const byBase = buildByBase(files);\n const rows = db.prepare('SELECT src, target, dst, embed FROM links').all() as unknown as LinkRow[];\n return resolveRows(db, rows, pathSet, byBase);\n}\n\n// Re-resolve only what this reconcile could have touched: re-stored sources, links whose\n// target basename matches an added/vanished file, and links whose dst just vanished. All\n// three are indexed lookups, so this never reads the whole table.\nfunction resolveIncremental(db: DatabaseSync, delta: ReconcileDelta): boolean {\n const pathSet = new Set(delta.files.map((f) => f.relPath));\n const byBase = buildByBase(delta.files);\n\n const changedBasenames = new Set<string>();\n for (const p of delta.added) changedBasenames.add(baseKey(p));\n for (const p of delta.vanished) changedBasenames.add(baseKey(p));\n\n // \\0 can't appear in a path or target, so the key never collides; a space can.\n const candidates = new Map<string, LinkRow>();\n const collect = (rows: LinkRow[]) => {\n for (const row of rows) candidates.set(`${row.src}\\0${row.target}\\0${row.embed}`, row);\n };\n // Chunked so a large-but-under-threshold delta never exceeds SQLite's bound-variable\n // limit (SQLITE_MAX_VARIABLE_NUMBER, 32766).\n const collectIn = (column: string, keys: string[]) => {\n for (let i = 0; i < keys.length; i += 500) {\n const chunk = keys.slice(i, i + 500);\n const placeholders = chunk.map(() => '?').join(', ');\n collect(db.prepare(`SELECT src, target, dst, embed FROM links WHERE ${column} IN (${placeholders})`).all(...chunk) as unknown as LinkRow[]);\n }\n };\n\n collectIn('src', delta.reparsed);\n collectIn('target_base', [...changedBasenames]);\n collectIn('dst', delta.vanished);\n\n return resolveRows(db, [...candidates.values()], pathSet, byBase);\n}\n\n// Resolved edges, for rank and for search's graph expansion.\nexport function linkEdges(db: DatabaseSync): [string, string][] {\n // One edge per row, as 0.12's grain always had it -- two written targets resolving to one\n // dst stay two edges (a weight the fusion evals were gated on). The only exclusion is an\n // embed whose exact (src, target) also exists as a link: that second row is new with the\n // embed grain and would double an edge that used to be one row. The NOT EXISTS probes the\n // primary key and fires only for embed rows; both branches still scan the table.\n const sql = `SELECT src, dst FROM links WHERE dst IS NOT NULL AND embed = 0\n UNION ALL\n SELECT src, dst FROM links l WHERE dst IS NOT NULL AND embed = 1\n AND NOT EXISTS (SELECT 1 FROM links l0 WHERE l0.src = l.src AND l0.target = l.target AND l0.embed = 0)`;\n return (db.prepare(sql).all() as Array<{ src: string; dst: string }>).map((r) => [r.src, r.dst]);\n}\n\n// remove() has already deleted the rows by the time afterReconcile runs, so it records here\n// whether they carried edges. Keyed on the delta object, so the state dies with the reconcile.\nconst removedWithLinks = new WeakMap<ReconcileDelta, Set<string>>();\n\nfunction recordRemoved(delta: ReconcileDelta, path: string): void {\n let set = removedWithLinks.get(delta);\n if (!set) {\n set = new Set();\n removedWithLinks.set(delta, set);\n }\n set.add(path);\n}\n\n// remove() runs per path; the vanished list is an array, so cache the Set per delta.\nconst vanishedSets = new WeakMap<ReconcileDelta, Set<string>>();\nfunction vanishedSet(delta: ReconcileDelta): Set<string> {\n let set = vanishedSets.get(delta);\n if (!set) {\n set = new Set(delta.vanished);\n vanishedSets.set(delta, set);\n }\n return set;\n}\n\nexport const links: Feature = {\n name: 'links',\n schema(db) {\n db.exec('CREATE TABLE IF NOT EXISTS links (src TEXT, target TEXT, target_base TEXT, dst TEXT, embed INTEGER, PRIMARY KEY (src, target, embed))');\n db.exec('CREATE INDEX IF NOT EXISTS links_dst ON links(dst)');\n db.exec('CREATE INDEX IF NOT EXISTS links_target_base ON links(target_base)');\n },\n extract,\n // Reparsed docs are diffed in store() below instead of wiped here: deleting and\n // re-inserting resets dst to NULL, which made every touch-only reparse read as an edge\n // change and recompute PageRank -- measured as most of the remaining update cost.\n remove(db, path, delta) {\n if (!vanishedSet(delta).has(path)) return;\n const result = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n if (Number(result.changes) > 0) recordRemoved(delta, path);\n },\n store(db, path, extracted, delta) {\n const targets = extracted as Array<{ target: string; embed: boolean }>;\n // Stale rows (target/embed pairs the new parse no longer contains) are real edge removals.\n // \\0 can't appear in a target, so the composite key never collides.\n let stale: ReturnType<ReturnType<DatabaseSync['prepare']>['run']>;\n if (targets.length === 0) {\n stale = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n } else {\n const placeholders = targets.map(() => '?').join(', ');\n const keys = targets.map((t) => `${t.target}\\0${t.embed ? '1' : '0'}`);\n stale = db.prepare(`DELETE FROM links WHERE src = ? AND (target || char(0) || embed) NOT IN (${placeholders})`).run(path, ...keys);\n }\n if (Number(stale.changes) > 0) recordRemoved(delta, path);\n // Upsert preserves dst on surviving rows, so an unchanged link resolves to the same\n // value and reports no change; only genuinely new rows start at NULL.\n const insert = db.prepare('INSERT INTO links (src, target, target_base, dst, embed) VALUES (?, ?, ?, NULL, ?) ON CONFLICT(src, target, embed) DO UPDATE SET target_base = excluded.target_base');\n for (const { target, embed } of targets) insert.run(path, target, baseKey(cleanTarget(target)), embed ? 1 : 0);\n },\n afterReconcile(db, delta) {\n // Any deleted rows that carried edges mean the edge set shrank -- whether the file\n // vanished or was reparsed down to fewer/no links. dstChanged alone misses the\n // reparsed-to-zero-links case (no surviving rows to re-resolve).\n const removeHadLinks = (removedWithLinks.get(delta)?.size ?? 0) > 0;\n\n const churn = delta.reparsed.length + delta.vanished.length;\n // Past this share of the tree, a full pass is the only one guaranteed to match\n // resolveTarget's ambiguity rules. A cold build clears the threshold on its own.\n const large = delta.files.length === 0 || churn > 0.2 * delta.files.length;\n\n const dstChanged = large ? resolveAll(db, delta.files) : resolveIncremental(db, delta);\n delta.linksChanged = dstChanged || removeHadLinks;\n },\n};\n"],"names":["linkEdges","links","extract","_raw","body","seen","Set","results","add","target","embed","key","has","push","matchAll","m","split","trim","index","test","cleanTarget","replace","baseKey","path","posix","basename","toLowerCase","resolveTarget","src","pathSet","byBase","clean","fromSrc","normalize","join","dirname","candidate","get","buildByBase","files","Map","map","f","relPath","sort","list","set","resolveRows","db","rows","update","prepare","changed","row","dst","run","resolveAll","all","resolveIncremental","delta","changedBasenames","added","p","vanished","candidates","collect","collectIn","column","keys","i","length","chunk","slice","placeholders","reparsed","values","sql","r","removedWithLinks","WeakMap","recordRemoved","vanishedSets","vanishedSet","name","schema","exec","remove","result","Number","changes","store","extracted","targets","stale","t","insert","afterReconcile","removeHadLinks","size","churn","large","dstChanged","linksChanged"],"mappings":"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"}
|
|
@@ -8,17 +8,15 @@ Object.defineProperty(exports, "sections", {
|
|
|
8
8
|
return sections;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
+
var _fencests = require("../fences.js");
|
|
11
12
|
// Headings outside fenced code blocks.
|
|
12
13
|
function extract(raw) {
|
|
13
14
|
var lines = raw.split('\n');
|
|
14
15
|
var found = [];
|
|
15
|
-
var
|
|
16
|
+
var fence = (0, _fencests.fenceTracker)();
|
|
16
17
|
for(var i = 0; i < lines.length; i++){
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
if (inFence) continue;
|
|
18
|
+
if (fence.feed(lines[i])) continue;
|
|
19
|
+
if (fence.inFence) continue;
|
|
22
20
|
var m = lines[i].match(/^(#{1,6}) +(.*)/);
|
|
23
21
|
if (m) found.push({
|
|
24
22
|
level: m[1].length,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/sections.ts"],"sourcesContent":["import type { Feature } from './types.ts';\n\n// sections(path, idx, level, heading, start_line, end_line, tokens): the heading outline,\n// 1-indexed over the raw file so a row is a direct Read range; tokens is a chars/4 estimate.\n\nexport interface Section {\n level: number;\n heading: string;\n startLine: number;\n endLine: number;\n tokens: number;\n}\n\n// Headings outside fenced code blocks.\nfunction extract(raw: string): Section[] {\n const lines = raw.split('\\n');\n const found: Section[] = [];\n
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/sections.ts"],"sourcesContent":["import { fenceTracker } from '../fences.ts';\nimport type { Feature } from './types.ts';\n\n// sections(path, idx, level, heading, start_line, end_line, tokens): the heading outline,\n// 1-indexed over the raw file so a row is a direct Read range; tokens is a chars/4 estimate.\n\nexport interface Section {\n level: number;\n heading: string;\n startLine: number;\n endLine: number;\n tokens: number;\n}\n\n// Headings outside fenced code blocks.\nfunction extract(raw: string): Section[] {\n const lines = raw.split('\\n');\n const found: Section[] = [];\n const fence = fenceTracker();\n for (let i = 0; i < lines.length; i++) {\n if (fence.feed(lines[i])) continue;\n if (fence.inFence) continue;\n const m = lines[i].match(/^(#{1,6}) +(.*)/);\n if (m) found.push({ level: m[1].length, heading: m[2].trim(), startLine: i + 1, endLine: lines.length, tokens: 0 });\n }\n for (let s = 0; s < found.length; s++) {\n if (s + 1 < found.length) found[s].endLine = found[s + 1].startLine - 1;\n const chars = lines.slice(found[s].startLine - 1, found[s].endLine).join('\\n').length;\n found[s].tokens = Math.ceil(chars / 4);\n }\n return found;\n}\n\nexport const sections: Feature = {\n name: 'sections',\n schema(db) {\n db.exec(`CREATE TABLE IF NOT EXISTS sections (\"path\" TEXT, idx INTEGER, level INTEGER, heading TEXT, start_line INTEGER, end_line INTEGER, tokens INTEGER, PRIMARY KEY (\"path\", idx))`);\n },\n extract,\n remove(db, path) {\n db.prepare('DELETE FROM sections WHERE \"path\" = ?').run(path);\n },\n store(db, path, extracted) {\n const insert = db.prepare('INSERT INTO sections (\"path\", idx, level, heading, start_line, end_line, tokens) VALUES (?, ?, ?, ?, ?, ?, ?)');\n (extracted as Section[]).forEach((s, idx) => insert.run(path, idx, s.level, s.heading, s.startLine, s.endLine, s.tokens));\n },\n};\n"],"names":["sections","extract","raw","lines","split","found","fence","fenceTracker","i","length","feed","inFence","m","match","push","level","heading","trim","startLine","endLine","tokens","s","chars","slice","join","Math","ceil","name","schema","db","exec","remove","path","prepare","run","store","extracted","insert","forEach","idx"],"mappings":";;;;+BAiCaA;;;eAAAA;;;wBAjCgB;AAc7B,uCAAuC;AACvC,SAASC,QAAQC,GAAW;IAC1B,IAAMC,QAAQD,IAAIE,KAAK,CAAC;IACxB,IAAMC,QAAmB,EAAE;IAC3B,IAAMC,QAAQC,IAAAA,sBAAY;IAC1B,IAAK,IAAIC,IAAI,GAAGA,IAAIL,MAAMM,MAAM,EAAED,IAAK;QACrC,IAAIF,MAAMI,IAAI,CAACP,KAAK,CAACK,EAAE,GAAG;QAC1B,IAAIF,MAAMK,OAAO,EAAE;QACnB,IAAMC,IAAIT,KAAK,CAACK,EAAE,CAACK,KAAK,CAAC;QACzB,IAAID,GAAGP,MAAMS,IAAI,CAAC;YAAEC,OAAOH,CAAC,CAAC,EAAE,CAACH,MAAM;YAAEO,SAASJ,CAAC,CAAC,EAAE,CAACK,IAAI;YAAIC,WAAWV,IAAI;YAAGW,SAAShB,MAAMM,MAAM;YAAEW,QAAQ;QAAE;IACnH;IACA,IAAK,IAAIC,IAAI,GAAGA,IAAIhB,MAAMI,MAAM,EAAEY,IAAK;QACrC,IAAIA,IAAI,IAAIhB,MAAMI,MAAM,EAAEJ,KAAK,CAACgB,EAAE,CAACF,OAAO,GAAGd,KAAK,CAACgB,IAAI,EAAE,CAACH,SAAS,GAAG;QACtE,IAAMI,QAAQnB,MAAMoB,KAAK,CAAClB,KAAK,CAACgB,EAAE,CAACH,SAAS,GAAG,GAAGb,KAAK,CAACgB,EAAE,CAACF,OAAO,EAAEK,IAAI,CAAC,MAAMf,MAAM;QACrFJ,KAAK,CAACgB,EAAE,CAACD,MAAM,GAAGK,KAAKC,IAAI,CAACJ,QAAQ;IACtC;IACA,OAAOjB;AACT;AAEO,IAAML,WAAoB;IAC/B2B,MAAM;IACNC,QAAAA,SAAAA,OAAOC,EAAE;QACPA,GAAGC,IAAI,CAAC;IACV;IACA7B,SAAAA;IACA8B,QAAAA,SAAAA,OAAOF,EAAE,EAAEG,IAAI;QACbH,GAAGI,OAAO,CAAC,yCAAyCC,GAAG,CAACF;IAC1D;IACAG,OAAAA,SAAAA,MAAMN,EAAE,EAAEG,IAAI,EAAEI,SAAS;QACvB,IAAMC,SAASR,GAAGI,OAAO,CAAC;QACzBG,UAAwBE,OAAO,CAAC,SAACjB,GAAGkB;mBAAQF,OAAOH,GAAG,CAACF,MAAMO,KAAKlB,EAAEN,KAAK,EAAEM,EAAEL,OAAO,EAAEK,EAAEH,SAAS,EAAEG,EAAEF,OAAO,EAAEE,EAAED,MAAM;;IACzH;AACF"}
|
|
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "tags", {
|
|
|
8
8
|
return tags;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
+
var _fencests = require("../fences.js");
|
|
11
12
|
function _array_like_to_array(arr, len) {
|
|
12
13
|
if (len == null || len > arr.length) len = arr.length;
|
|
13
14
|
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
@@ -36,10 +37,91 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
36
37
|
// tags(path, tag): Obsidian's file.tags grain -- frontmatter list/string tags plus inline
|
|
37
38
|
// #tags from the prose, deduplicated, source not distinguished. Nested tags store full
|
|
38
39
|
// (book/scifi); `tag = 'book' OR tag LIKE 'book/%'` is how a caller matches the parent too.
|
|
39
|
-
|
|
40
|
-
var
|
|
40
|
+
// Obsidian treats [[#Heading]] as a same-note link, not a tag.
|
|
41
|
+
var WIKILINK_RE = /\[\[.*?\]\]/g; // to the first ]], so a heading holding a lone ] still masks
|
|
42
|
+
// Obsidian doesn't read tags inside HTML markup.
|
|
43
|
+
var HTML_TAG_RE = /<\/?[a-zA-Z][^>]*>/g; // tag-shaped only: a comparison's `< 5` must not open a span
|
|
41
44
|
// Anchors on start-of-line or a preceding whitespace/(/[ so `a#b` and URL fragments don't count.
|
|
42
45
|
var INLINE_TAG_RE = RegExp("(?:^|[\\s([])#([\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}_/-]+)", "gu");
|
|
46
|
+
// A markdown link destination `](...)` -- `[text](#anchor)` is a same-page link, not a tag.
|
|
47
|
+
var LINK_DEST_RE = /\]\((?:[^()]|\([^()]*\))*\)/g; // one paren-nesting level, as CommonMark destinations allow: (https://x/a_(b)#frag)
|
|
48
|
+
// CommonMark's HTML-block type-6 list (fixed by the spec, not a drifting enumeration): a line
|
|
49
|
+
// starting with an open or close tag of one of these, at column 0, opens a block that swallows
|
|
50
|
+
// following lines -- including any #tag in them -- until a blank line closes it.
|
|
51
|
+
var HTML_BLOCK_TAGS = new Set([
|
|
52
|
+
'address',
|
|
53
|
+
'article',
|
|
54
|
+
'aside',
|
|
55
|
+
'base',
|
|
56
|
+
'basefont',
|
|
57
|
+
'blockquote',
|
|
58
|
+
'body',
|
|
59
|
+
'caption',
|
|
60
|
+
'center',
|
|
61
|
+
'col',
|
|
62
|
+
'colgroup',
|
|
63
|
+
'dd',
|
|
64
|
+
'details',
|
|
65
|
+
'dialog',
|
|
66
|
+
'dir',
|
|
67
|
+
'div',
|
|
68
|
+
'dl',
|
|
69
|
+
'dt',
|
|
70
|
+
'fieldset',
|
|
71
|
+
'figcaption',
|
|
72
|
+
'figure',
|
|
73
|
+
'footer',
|
|
74
|
+
'form',
|
|
75
|
+
'frame',
|
|
76
|
+
'frameset',
|
|
77
|
+
'h1',
|
|
78
|
+
'h2',
|
|
79
|
+
'h3',
|
|
80
|
+
'h4',
|
|
81
|
+
'h5',
|
|
82
|
+
'h6',
|
|
83
|
+
'head',
|
|
84
|
+
'header',
|
|
85
|
+
'hr',
|
|
86
|
+
'html',
|
|
87
|
+
'iframe',
|
|
88
|
+
'legend',
|
|
89
|
+
'li',
|
|
90
|
+
'link',
|
|
91
|
+
'main',
|
|
92
|
+
'menu',
|
|
93
|
+
'menuitem',
|
|
94
|
+
'nav',
|
|
95
|
+
'noframes',
|
|
96
|
+
'ol',
|
|
97
|
+
'optgroup',
|
|
98
|
+
'option',
|
|
99
|
+
'p',
|
|
100
|
+
'param',
|
|
101
|
+
'search',
|
|
102
|
+
'section',
|
|
103
|
+
'summary',
|
|
104
|
+
'table',
|
|
105
|
+
'tbody',
|
|
106
|
+
'td',
|
|
107
|
+
'tfoot',
|
|
108
|
+
'th',
|
|
109
|
+
'thead',
|
|
110
|
+
'title',
|
|
111
|
+
'tr',
|
|
112
|
+
'track',
|
|
113
|
+
'ul'
|
|
114
|
+
]);
|
|
115
|
+
// Type-1 blocks (script/pre/style/textarea): closes on the line holding the matching end tag,
|
|
116
|
+
// not on a blank line, and that line is the last one skipped.
|
|
117
|
+
var HTML_PRE_TAGS = new Set([
|
|
118
|
+
'script',
|
|
119
|
+
'pre',
|
|
120
|
+
'style',
|
|
121
|
+
'textarea'
|
|
122
|
+
]);
|
|
123
|
+
// An opening or closing tag at column 0, tag name captured for the lookups above.
|
|
124
|
+
var HTML_BLOCK_OPEN_RE = /^<\/?([a-zA-Z][a-zA-Z0-9]*)(?:[ \t]|\/?>|$)/;
|
|
43
125
|
// Strips a leading # (frontmatter entries may carry one) and a trailing /; rejects an
|
|
44
126
|
// all-digit result -- a tag needs at least one non-digit character.
|
|
45
127
|
function normalizeTag(raw) {
|
|
@@ -79,23 +161,104 @@ function frontmatterTags(data) {
|
|
|
79
161
|
}
|
|
80
162
|
return found;
|
|
81
163
|
}
|
|
82
|
-
//
|
|
164
|
+
// A code span opens on a run of N backticks and closes at the next run of exactly N -- a
|
|
165
|
+
// shorter or longer run in between is literal text, not a delimiter (CommonMark code spans).
|
|
166
|
+
// Masked with spaces so column positions and tag-boundary whitespace are unaffected.
|
|
167
|
+
function maskCodeSpans(line) {
|
|
168
|
+
var out = '';
|
|
169
|
+
var i = 0;
|
|
170
|
+
while(i < line.length){
|
|
171
|
+
if (line[i] !== '`') {
|
|
172
|
+
out += line[i];
|
|
173
|
+
i++;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
var j = i;
|
|
177
|
+
while(line[j] === '`')j++;
|
|
178
|
+
var n = j - i;
|
|
179
|
+
var k = j;
|
|
180
|
+
var closeStart = -1;
|
|
181
|
+
var closeEnd = -1;
|
|
182
|
+
while(k < line.length){
|
|
183
|
+
if (line[k] !== '`') {
|
|
184
|
+
k++;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
var m = k;
|
|
188
|
+
while(line[m] === '`')m++;
|
|
189
|
+
if (m - k === n) {
|
|
190
|
+
closeStart = k;
|
|
191
|
+
closeEnd = m;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
k = m;
|
|
195
|
+
}
|
|
196
|
+
if (closeStart >= 0) {
|
|
197
|
+
out += ' '.repeat(closeEnd - i);
|
|
198
|
+
i = closeEnd;
|
|
199
|
+
} else {
|
|
200
|
+
out += line.slice(i, j);
|
|
201
|
+
i = j;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
// #tag tokens outside fenced code blocks, inline code spans, wikilinks, HTML tags, HTML blocks,
|
|
207
|
+
// and link destinations.
|
|
83
208
|
function inlineTags(body) {
|
|
84
209
|
var found = [];
|
|
85
|
-
var
|
|
210
|
+
var fence = (0, _fencests.fenceTracker)();
|
|
211
|
+
var inHtmlBlock = false;
|
|
212
|
+
var htmlBlockClose = null; // set while inside a type-1 (script/pre/style/textarea) block
|
|
86
213
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
87
214
|
try {
|
|
88
215
|
for(var _iterator = body.split('\n')[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
89
216
|
var line = _step.value;
|
|
90
|
-
if (
|
|
91
|
-
|
|
217
|
+
if (inHtmlBlock) {
|
|
218
|
+
// A fence-like line here is still HTML-block content -- the block wins until it closes.
|
|
219
|
+
if (htmlBlockClose) {
|
|
220
|
+
if (htmlBlockClose.test(line)) {
|
|
221
|
+
inHtmlBlock = false;
|
|
222
|
+
htmlBlockClose = null;
|
|
223
|
+
}
|
|
224
|
+
} else if (/^[ \t>]*$/.test(line)) {
|
|
225
|
+
inHtmlBlock = false;
|
|
226
|
+
}
|
|
92
227
|
continue;
|
|
93
228
|
}
|
|
94
|
-
if (
|
|
229
|
+
if (fence.feed(line)) continue;
|
|
230
|
+
if (fence.inFence) continue;
|
|
231
|
+
// Indented or blockquoted HTML blocks still swallow their content in Obsidian, so the
|
|
232
|
+
// opener test runs after stripping leading whitespace and > markers.
|
|
233
|
+
var stripped = line.replace(/^[ \t>]*/, '');
|
|
234
|
+
if (stripped[0] === '<') {
|
|
235
|
+
var openMatch = HTML_BLOCK_OPEN_RE.exec(stripped);
|
|
236
|
+
if (openMatch) {
|
|
237
|
+
var tagName = openMatch[1].toLowerCase();
|
|
238
|
+
var isClosingTag = stripped[1] === '/';
|
|
239
|
+
if (!isClosingTag && HTML_PRE_TAGS.has(tagName)) {
|
|
240
|
+
inHtmlBlock = true;
|
|
241
|
+
// Any of the four type-1 closers ends the block, not only the tag that opened it.
|
|
242
|
+
htmlBlockClose = /<\/(?:script|pre|style|textarea)>/i;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (HTML_BLOCK_TAGS.has(tagName)) {
|
|
246
|
+
inHtmlBlock = true;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
95
251
|
if (!line.includes('#')) continue; // most lines; skip the regex work
|
|
96
|
-
var cleaned = line.includes('`') ? line
|
|
252
|
+
var cleaned = line.includes('`') ? maskCodeSpans(line) : line;
|
|
253
|
+
if (cleaned.includes('[[')) cleaned = cleaned.replace(WIKILINK_RE, function(m) {
|
|
254
|
+
return ' '.repeat(m.length);
|
|
255
|
+
});
|
|
256
|
+
if (cleaned.includes('](')) cleaned = cleaned.replace(LINK_DEST_RE, function(m) {
|
|
257
|
+
return ' '.repeat(m.length);
|
|
258
|
+
});
|
|
259
|
+
if (cleaned.includes('<')) cleaned = cleaned.replace(HTML_TAG_RE, function(m) {
|
|
97
260
|
return ' '.repeat(m.length);
|
|
98
|
-
})
|
|
261
|
+
});
|
|
99
262
|
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
100
263
|
try {
|
|
101
264
|
for(var _iterator1 = cleaned.matchAll(INLINE_TAG_RE)[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/tags.ts"],"sourcesContent":["import type { Feature } from './types.ts';\n\n// tags(path, tag): Obsidian's file.tags grain -- frontmatter list/string tags plus inline\n// #tags from the prose, deduplicated, source not distinguished. Nested tags store full\n// (book/scifi); `tag = 'book' OR tag LIKE 'book/%'` is how a caller matches the parent too.\n\nconst FENCE_RE = /^(```|~~~)/;\nconst INLINE_CODE_RE = /`[^`]*`/g;\n// Anchors on start-of-line or a preceding whitespace/(/[ so `a#b` and URL fragments don't count.\nconst INLINE_TAG_RE = /(?:^|[\\s([])#([\\p{L}\\p{N}_/-]+)/gu;\n\n// Strips a leading # (frontmatter entries may carry one) and a trailing /; rejects an\n// all-digit result -- a tag needs at least one non-digit character.\nfunction normalizeTag(raw: string): string | null {\n const stripped = raw.replace(/^#/, '').replace(/\\/+$/, '');\n if (!stripped || /^\\d+$/.test(stripped)) return null;\n return stripped;\n}\n\n// data.tags: a YAML list (Obsidian also accepts a bare string). Null members and non-string\n// members are skipped rather than throwing -- `tags:\\n -` parses to [null].\nfunction frontmatterTags(data?: Record<string, unknown>): string[] {\n const raw = data?.tags;\n const list = Array.isArray(raw) ? raw : typeof raw === 'string' ? [raw] : [];\n const found: string[] = [];\n for (const item of list) {\n if (typeof item !== 'string') continue;\n const tag = normalizeTag(item);\n if (tag) found.push(tag);\n }\n return found;\n}\n\n// #tag tokens outside fenced code blocks and inline code spans.\nfunction inlineTags(body: string): string[] {\n const found: string[] = [];\n let inFence = false;\n for (const line of body.split('\\n')) {\n if (FENCE_RE.test(line)) {\n inFence = !inFence;\n continue;\n }\n if (inFence) continue;\n if (!line.includes('#')) continue; // most lines; skip the regex work\n const cleaned = line.includes('`') ? line.replace(INLINE_CODE_RE, (m) => ' '.repeat(m.length)) : line;\n for (const m of cleaned.matchAll(INLINE_TAG_RE)) {\n const tag = normalizeTag(m[1]);\n if (tag) found.push(tag);\n }\n }\n return found;\n}\n\nfunction extract(_raw: string, body: string, _search?: { title: string; summary: string }, data?: Record<string, unknown>): string[] {\n return [...new Set([...frontmatterTags(data), ...inlineTags(body)])].sort();\n}\n\nexport const tags: Feature = {\n name: 'tags',\n schema(db) {\n db.exec('CREATE TABLE IF NOT EXISTS tags (\"path\" TEXT, tag TEXT, PRIMARY KEY (\"path\", tag))');\n db.exec('CREATE INDEX IF NOT EXISTS tags_tag ON tags(tag)');\n },\n extract,\n // Per-file rows with nothing else to resolve, so only a vanished file needs a delete here;\n // store() below handles a reparse's stale rows itself.\n remove(db, path, delta) {\n if (!delta.vanished.includes(path)) return;\n db.prepare('DELETE FROM tags WHERE \"path\" = ?').run(path);\n },\n store(db, path, extracted) {\n const found = extracted as string[];\n if (found.length === 0) {\n db.prepare('DELETE FROM tags WHERE \"path\" = ?').run(path);\n } else {\n const placeholders = found.map(() => '?').join(', ');\n db.prepare(`DELETE FROM tags WHERE \"path\" = ? AND tag NOT IN (${placeholders})`).run(path, ...found);\n }\n const insert = db.prepare('INSERT OR IGNORE INTO tags (\"path\", tag) VALUES (?, ?)');\n for (const tag of found) insert.run(path, tag);\n },\n};\n"],"names":["tags","FENCE_RE","INLINE_CODE_RE","INLINE_TAG_RE","normalizeTag","raw","stripped","replace","test","frontmatterTags","data","list","Array","isArray","found","item","tag","push","inlineTags","body","inFence","split","line","includes","cleaned","m","repeat","length","matchAll","extract","_raw","_search","Set","sort","name","schema","db","exec","remove","path","delta","vanished","prepare","run","store","extracted","placeholders","map","join","insert"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/tags.ts"],"sourcesContent":["import { fenceTracker } from '../fences.ts';\nimport type { Feature } from './types.ts';\n\n// tags(path, tag): Obsidian's file.tags grain -- frontmatter list/string tags plus inline\n// #tags from the prose, deduplicated, source not distinguished. Nested tags store full\n// (book/scifi); `tag = 'book' OR tag LIKE 'book/%'` is how a caller matches the parent too.\n\n// Obsidian treats [[#Heading]] as a same-note link, not a tag.\nconst WIKILINK_RE = /\\[\\[.*?\\]\\]/g; // to the first ]], so a heading holding a lone ] still masks\n// Obsidian doesn't read tags inside HTML markup.\nconst HTML_TAG_RE = /<\\/?[a-zA-Z][^>]*>/g; // tag-shaped only: a comparison's `< 5` must not open a span\n// Anchors on start-of-line or a preceding whitespace/(/[ so `a#b` and URL fragments don't count.\nconst INLINE_TAG_RE = /(?:^|[\\s([])#([\\p{L}\\p{N}_/-]+)/gu;\n// A markdown link destination `](...)` -- `[text](#anchor)` is a same-page link, not a tag.\nconst LINK_DEST_RE = /\\]\\((?:[^()]|\\([^()]*\\))*\\)/g; // one paren-nesting level, as CommonMark destinations allow: (https://x/a_(b)#frag)\n\n// CommonMark's HTML-block type-6 list (fixed by the spec, not a drifting enumeration): a line\n// starting with an open or close tag of one of these, at column 0, opens a block that swallows\n// following lines -- including any #tag in them -- until a blank line closes it.\nconst HTML_BLOCK_TAGS = new Set([\n 'address',\n 'article',\n 'aside',\n 'base',\n 'basefont',\n 'blockquote',\n 'body',\n 'caption',\n 'center',\n 'col',\n 'colgroup',\n 'dd',\n 'details',\n 'dialog',\n 'dir',\n 'div',\n 'dl',\n 'dt',\n 'fieldset',\n 'figcaption',\n 'figure',\n 'footer',\n 'form',\n 'frame',\n 'frameset',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'head',\n 'header',\n 'hr',\n 'html',\n 'iframe',\n 'legend',\n 'li',\n 'link',\n 'main',\n 'menu',\n 'menuitem',\n 'nav',\n 'noframes',\n 'ol',\n 'optgroup',\n 'option',\n 'p',\n 'param',\n 'search',\n 'section',\n 'summary',\n 'table',\n 'tbody',\n 'td',\n 'tfoot',\n 'th',\n 'thead',\n 'title',\n 'tr',\n 'track',\n 'ul',\n]);\n// Type-1 blocks (script/pre/style/textarea): closes on the line holding the matching end tag,\n// not on a blank line, and that line is the last one skipped.\nconst HTML_PRE_TAGS = new Set(['script', 'pre', 'style', 'textarea']);\n// An opening or closing tag at column 0, tag name captured for the lookups above.\nconst HTML_BLOCK_OPEN_RE = /^<\\/?([a-zA-Z][a-zA-Z0-9]*)(?:[ \\t]|\\/?>|$)/;\n\n// Strips a leading # (frontmatter entries may carry one) and a trailing /; rejects an\n// all-digit result -- a tag needs at least one non-digit character.\nfunction normalizeTag(raw: string): string | null {\n const stripped = raw.replace(/^#/, '').replace(/\\/+$/, '');\n if (!stripped || /^\\d+$/.test(stripped)) return null;\n return stripped;\n}\n\n// data.tags: a YAML list (Obsidian also accepts a bare string). Null members and non-string\n// members are skipped rather than throwing -- `tags:\\n -` parses to [null].\nfunction frontmatterTags(data?: Record<string, unknown>): string[] {\n const raw = data?.tags;\n const list = Array.isArray(raw) ? raw : typeof raw === 'string' ? [raw] : [];\n const found: string[] = [];\n for (const item of list) {\n if (typeof item !== 'string') continue;\n const tag = normalizeTag(item);\n if (tag) found.push(tag);\n }\n return found;\n}\n\n// A code span opens on a run of N backticks and closes at the next run of exactly N -- a\n// shorter or longer run in between is literal text, not a delimiter (CommonMark code spans).\n// Masked with spaces so column positions and tag-boundary whitespace are unaffected.\nfunction maskCodeSpans(line: string): string {\n let out = '';\n let i = 0;\n while (i < line.length) {\n if (line[i] !== '`') {\n out += line[i];\n i++;\n continue;\n }\n let j = i;\n while (line[j] === '`') j++;\n const n = j - i;\n let k = j;\n let closeStart = -1;\n let closeEnd = -1;\n while (k < line.length) {\n if (line[k] !== '`') {\n k++;\n continue;\n }\n let m = k;\n while (line[m] === '`') m++;\n if (m - k === n) {\n closeStart = k;\n closeEnd = m;\n break;\n }\n k = m;\n }\n if (closeStart >= 0) {\n out += ' '.repeat(closeEnd - i);\n i = closeEnd;\n } else {\n out += line.slice(i, j);\n i = j;\n }\n }\n return out;\n}\n\n// #tag tokens outside fenced code blocks, inline code spans, wikilinks, HTML tags, HTML blocks,\n// and link destinations.\nfunction inlineTags(body: string): string[] {\n const found: string[] = [];\n const fence = fenceTracker();\n let inHtmlBlock = false;\n let htmlBlockClose: RegExp | null = null; // set while inside a type-1 (script/pre/style/textarea) block\n for (const line of body.split('\\n')) {\n if (inHtmlBlock) {\n // A fence-like line here is still HTML-block content -- the block wins until it closes.\n if (htmlBlockClose) {\n if (htmlBlockClose.test(line)) {\n inHtmlBlock = false;\n htmlBlockClose = null;\n }\n } else if (/^[ \\t>]*$/.test(line)) {\n inHtmlBlock = false;\n }\n continue;\n }\n if (fence.feed(line)) continue;\n if (fence.inFence) continue;\n // Indented or blockquoted HTML blocks still swallow their content in Obsidian, so the\n // opener test runs after stripping leading whitespace and > markers.\n const stripped = line.replace(/^[ \\t>]*/, '');\n if (stripped[0] === '<') {\n const openMatch = HTML_BLOCK_OPEN_RE.exec(stripped);\n if (openMatch) {\n const tagName = openMatch[1].toLowerCase();\n const isClosingTag = stripped[1] === '/';\n if (!isClosingTag && HTML_PRE_TAGS.has(tagName)) {\n inHtmlBlock = true;\n // Any of the four type-1 closers ends the block, not only the tag that opened it.\n htmlBlockClose = /<\\/(?:script|pre|style|textarea)>/i;\n continue;\n }\n if (HTML_BLOCK_TAGS.has(tagName)) {\n inHtmlBlock = true;\n continue;\n }\n }\n }\n if (!line.includes('#')) continue; // most lines; skip the regex work\n let cleaned = line.includes('`') ? maskCodeSpans(line) : line;\n if (cleaned.includes('[[')) cleaned = cleaned.replace(WIKILINK_RE, (m) => ' '.repeat(m.length));\n if (cleaned.includes('](')) cleaned = cleaned.replace(LINK_DEST_RE, (m) => ' '.repeat(m.length));\n if (cleaned.includes('<')) cleaned = cleaned.replace(HTML_TAG_RE, (m) => ' '.repeat(m.length));\n for (const m of cleaned.matchAll(INLINE_TAG_RE)) {\n const tag = normalizeTag(m[1]);\n if (tag) found.push(tag);\n }\n }\n return found;\n}\n\nfunction extract(_raw: string, body: string, _search?: { title: string; summary: string }, data?: Record<string, unknown>): string[] {\n return [...new Set([...frontmatterTags(data), ...inlineTags(body)])].sort();\n}\n\nexport const tags: Feature = {\n name: 'tags',\n schema(db) {\n db.exec('CREATE TABLE IF NOT EXISTS tags (\"path\" TEXT, tag TEXT, PRIMARY KEY (\"path\", tag))');\n db.exec('CREATE INDEX IF NOT EXISTS tags_tag ON tags(tag)');\n },\n extract,\n // Per-file rows with nothing else to resolve, so only a vanished file needs a delete here;\n // store() below handles a reparse's stale rows itself.\n remove(db, path, delta) {\n if (!delta.vanished.includes(path)) return;\n db.prepare('DELETE FROM tags WHERE \"path\" = ?').run(path);\n },\n store(db, path, extracted) {\n const found = extracted as string[];\n if (found.length === 0) {\n db.prepare('DELETE FROM tags WHERE \"path\" = ?').run(path);\n } else {\n const placeholders = found.map(() => '?').join(', ');\n db.prepare(`DELETE FROM tags WHERE \"path\" = ? AND tag NOT IN (${placeholders})`).run(path, ...found);\n }\n const insert = db.prepare('INSERT OR IGNORE INTO tags (\"path\", tag) VALUES (?, ?)');\n for (const tag of found) insert.run(path, tag);\n },\n};\n"],"names":["tags","WIKILINK_RE","HTML_TAG_RE","INLINE_TAG_RE","LINK_DEST_RE","HTML_BLOCK_TAGS","Set","HTML_PRE_TAGS","HTML_BLOCK_OPEN_RE","normalizeTag","raw","stripped","replace","test","frontmatterTags","data","list","Array","isArray","found","item","tag","push","maskCodeSpans","line","out","i","length","j","n","k","closeStart","closeEnd","m","repeat","slice","inlineTags","body","fence","fenceTracker","inHtmlBlock","htmlBlockClose","split","feed","inFence","openMatch","exec","tagName","toLowerCase","isClosingTag","has","includes","cleaned","matchAll","extract","_raw","_search","sort","name","schema","db","remove","path","delta","vanished","prepare","run","store","extracted","placeholders","map","join","insert"],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Shared line-fence tracker for tags/sections/embed. Opener: >=3 backticks or tildes at column
|
|
2
|
+
// 0 -- no indent allowance, a deliberate divergence from CommonMark's up-to-3-space rule (see
|
|
3
|
+
// test/unit/fences.test.ts DIVERGENCES table). A backtick opener's info string must not itself
|
|
4
|
+
// contain a backtick (spec rule). A closer is a run of the SAME character, length >= the
|
|
5
|
+
// opener's, holding nothing but trailing spaces after the run.
|
|
6
|
+
"use strict";
|
|
7
|
+
Object.defineProperty(exports, "__esModule", {
|
|
8
|
+
value: true
|
|
9
|
+
});
|
|
10
|
+
Object.defineProperty(exports, "fenceTracker", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function() {
|
|
13
|
+
return fenceTracker;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
var BACKTICK_OPEN = /^(`{3,})(.*)$/;
|
|
17
|
+
var TILDE_OPEN = /^(~{3,})(.*)$/;
|
|
18
|
+
function fenceTracker() {
|
|
19
|
+
var inFence = false;
|
|
20
|
+
var fenceChar = '';
|
|
21
|
+
var fenceLen = 0;
|
|
22
|
+
return {
|
|
23
|
+
feed: function feed(line) {
|
|
24
|
+
if (!inFence) {
|
|
25
|
+
var bt = BACKTICK_OPEN.exec(line);
|
|
26
|
+
if (bt && !bt[2].includes('`')) {
|
|
27
|
+
inFence = true;
|
|
28
|
+
fenceChar = '`';
|
|
29
|
+
fenceLen = bt[1].length;
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
var td = TILDE_OPEN.exec(line);
|
|
33
|
+
if (td) {
|
|
34
|
+
inFence = true;
|
|
35
|
+
fenceChar = '~';
|
|
36
|
+
fenceLen = td[1].length;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
var run = fenceChar === '`' ? /^(`+)(.*)$/ : /^(~+)(.*)$/;
|
|
42
|
+
var m = run.exec(line);
|
|
43
|
+
if (m && m[1].length >= fenceLen && m[2].trim() === '') {
|
|
44
|
+
inFence = false;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
},
|
|
49
|
+
get inFence () {
|
|
50
|
+
return inFence;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/fences.ts"],"sourcesContent":["// Shared line-fence tracker for tags/sections/embed. Opener: >=3 backticks or tildes at column\n// 0 -- no indent allowance, a deliberate divergence from CommonMark's up-to-3-space rule (see\n// test/unit/fences.test.ts DIVERGENCES table). A backtick opener's info string must not itself\n// contain a backtick (spec rule). A closer is a run of the SAME character, length >= the\n// opener's, holding nothing but trailing spaces after the run.\n\nexport interface FenceTracker {\n feed(line: string): boolean; // true iff this line is a fence delimiter (open or close)\n readonly inFence: boolean;\n}\n\nconst BACKTICK_OPEN = /^(`{3,})(.*)$/;\nconst TILDE_OPEN = /^(~{3,})(.*)$/;\n\nexport function fenceTracker(): FenceTracker {\n let inFence = false;\n let fenceChar = '';\n let fenceLen = 0;\n return {\n feed(line: string): boolean {\n if (!inFence) {\n const bt = BACKTICK_OPEN.exec(line);\n if (bt && !bt[2].includes('`')) {\n inFence = true;\n fenceChar = '`';\n fenceLen = bt[1].length;\n return true;\n }\n const td = TILDE_OPEN.exec(line);\n if (td) {\n inFence = true;\n fenceChar = '~';\n fenceLen = td[1].length;\n return true;\n }\n return false;\n }\n const run = fenceChar === '`' ? /^(`+)(.*)$/ : /^(~+)(.*)$/;\n const m = run.exec(line);\n if (m && m[1].length >= fenceLen && m[2].trim() === '') {\n inFence = false;\n return true;\n }\n return false;\n },\n get inFence() {\n return inFence;\n },\n };\n}\n"],"names":["fenceTracker","BACKTICK_OPEN","TILDE_OPEN","inFence","fenceChar","fenceLen","feed","line","bt","exec","includes","length","td","run","m","trim"],"mappings":"AAAA,+FAA+F;AAC/F,8FAA8F;AAC9F,+FAA+F;AAC/F,yFAAyF;AACzF,+DAA+D;;;;;+BAU/CA;;;eAAAA;;;AAHhB,IAAMC,gBAAgB;AACtB,IAAMC,aAAa;AAEZ,SAASF;IACd,IAAIG,UAAU;IACd,IAAIC,YAAY;IAChB,IAAIC,WAAW;IACf,OAAO;QACLC,MAAAA,SAAAA,KAAKC,IAAY;YACf,IAAI,CAACJ,SAAS;gBACZ,IAAMK,KAAKP,cAAcQ,IAAI,CAACF;gBAC9B,IAAIC,MAAM,CAACA,EAAE,CAAC,EAAE,CAACE,QAAQ,CAAC,MAAM;oBAC9BP,UAAU;oBACVC,YAAY;oBACZC,WAAWG,EAAE,CAAC,EAAE,CAACG,MAAM;oBACvB,OAAO;gBACT;gBACA,IAAMC,KAAKV,WAAWO,IAAI,CAACF;gBAC3B,IAAIK,IAAI;oBACNT,UAAU;oBACVC,YAAY;oBACZC,WAAWO,EAAE,CAAC,EAAE,CAACD,MAAM;oBACvB,OAAO;gBACT;gBACA,OAAO;YACT;YACA,IAAME,MAAMT,cAAc,MAAM,eAAe;YAC/C,IAAMU,IAAID,IAAIJ,IAAI,CAACF;YACnB,IAAIO,KAAKA,CAAC,CAAC,EAAE,CAACH,MAAM,IAAIN,YAAYS,CAAC,CAAC,EAAE,CAACC,IAAI,OAAO,IAAI;gBACtDZ,UAAU;gBACV,OAAO;YACT;YACA,OAAO;QACT;QACA,IAAIA,WAAU;YACZ,OAAOA;QACT;IACF;AACF"}
|
package/dist/esm/cli/shared.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
|
+
import { columnHint } from '../column-hint.js';
|
|
2
3
|
import { resolvePreset } from '../config/index.js';
|
|
3
4
|
import { open } from '../db/index.js';
|
|
4
5
|
import { printRowStream } from '../output.js';
|
|
@@ -176,12 +177,13 @@ export function runSql(cfg, sql, params, format, label, preset) {
|
|
|
176
177
|
const columns = statement.columns().map((c)=>c.name);
|
|
177
178
|
printRowStream(statement.iterate(...params), format, columns);
|
|
178
179
|
} catch (err) {
|
|
180
|
+
const hinted = columnHint(db, err); // pragma needs the db still open
|
|
179
181
|
db.close();
|
|
180
182
|
// A saved query or ad-hoc SQL can carry `content MATCH ?` too, so the same FTS5
|
|
181
183
|
// punctuation trap applies -- the bound parameters are the search terms there. SQL
|
|
182
184
|
// without MATCH gets its error verbatim; search advice on a plain typo would mislead.
|
|
183
|
-
if (/\bMATCH\b/i.test(sql)) throw searchError(
|
|
184
|
-
throw
|
|
185
|
+
if (/\bMATCH\b/i.test(sql)) throw searchError(hinted, params.join(' '));
|
|
186
|
+
throw hinted;
|
|
185
187
|
}
|
|
186
188
|
db.close();
|
|
187
189
|
}
|