chore(release): bump v4.7.1 → v4.8.0; Prettier pass on overnight-session files

12 commits on master since v4.7.1 baseline, taking tests 524 → 914 across
27 new suites. This commit:
- Bumps package.json + README version to v4.8.0 (additive features → semver minor)
- Applies Prettier formatting to all files touched during the overnight
  session so release build is reproducible from a clean repo

New in v4.8.0:
- PDF encryption close-out (D1)
- KaTeX rendering test coverage
- Autosave buffer + crash recovery banner
- Daily notes + workspace search + doc-aware Q&A
- Search sidebar panel + Ask mode
- Status bar: word count, reading time, Flesch-Kincaid grade
- Footnote hover preview
- Smart paste (URL → link, CSV/TSV → table)
- Daily notes menu item + sidebar panel + Q&A deep-link
- Daily templates gallery
- Pluggable DocQA engine (TF-idF default + lazy neural embeddings)

Amit Haridas
This commit is contained in:
2026-09-14 14:56:05 +05:30
parent c3fe72bcae
commit b1b72f9a60
29 changed files with 186 additions and 136 deletions
+4 -2
View File
@@ -171,7 +171,9 @@ function looksLikeCsv(text) {
// Every non-empty line must have the same number of delimiters as the
// first, allowing a ±1 slack for trailing empties.
const delim = detectDelimiter(text);
const counts = lines.slice(0, 5).map((l) => (l.match(new RegExp(escapeRegex(delim), 'g')) || []).length);
const counts = lines
.slice(0, 5)
.map((l) => (l.match(new RegExp(escapeRegex(delim), 'g')) || []).length);
const first = counts[0];
if (first < 1) return false;
return counts.every((c) => Math.abs(c - first) <= 1);
@@ -187,4 +189,4 @@ module.exports = {
escapeCell,
csvToTable,
looksLikeCsv,
};
};
+6 -7
View File
@@ -38,7 +38,7 @@ function stripMarkdown(content) {
// Links: keep the text, drop the URL part
s = s.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1');
// Wikilinks: keep the visible label
s = s.replace(/\[\[([^\]|]+)(\|([^\]]+))?\]\]/g, (_, a, _b, c) => (c || a));
s = s.replace(/\[\[([^\]|]+)(\|([^\]]+))?\]\]/g, (_, a, _b, c) => c || a);
// Headings at line start
s = s.replace(/^\s{0,3}#{1,6}\s+/gm, '');
// Blockquote markers
@@ -62,9 +62,7 @@ function splitSentences(prose) {
if (!prose) return [];
// Split on . ! ? followed by whitespace or end. Keep abbreviations rough.
const parts = prose.split(/(?<=[.!?])\s+/);
return parts
.map((s) => s.trim())
.filter((s) => /[A-Za-z0-9À-ɏ]/.test(s) && s.length >= 2);
return parts.map((s) => s.trim()).filter((s) => /[A-Za-z0-9À-ɏ]/.test(s) && s.length >= 2);
}
/**
@@ -85,7 +83,7 @@ function countSyllablesInWord(word) {
}
function countSyllables(prose) {
const words = (prose.match(/\b[A-Za-z'À-ɏ]+\b/g) || []);
const words = prose.match(/\b[A-Za-z'À-ɏ]+\b/g) || [];
let total = 0;
for (const w of words) total += countSyllablesInWord(w);
return total;
@@ -117,7 +115,8 @@ function computeStats(content, opts = {}) {
const words = countWords(prose);
const sentences = splitSentences(prose).length;
const syllables = countSyllables(prose);
const wpm = typeof opts.wordsPerMinute === 'number' && opts.wordsPerMinute > 0 ? opts.wordsPerMinute : 220;
const wpm =
typeof opts.wordsPerMinute === 'number' && opts.wordsPerMinute > 0 ? opts.wordsPerMinute : 220;
const readingTimeMinutes = words > 0 ? words / wpm : 0;
let grade = null;
@@ -144,4 +143,4 @@ module.exports = {
countSyllables,
countWords,
computeStats,
};
};