sensemaking 0.22.2 → 0.23.1
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/README.md +6 -8
- package/dist/cjs/chunk/extract.d.cts +3 -3
- package/dist/cjs/chunk/extract.d.ts +3 -3
- package/dist/cjs/chunk/extract.js +369 -72
- package/dist/cjs/chunk/extract.js.map +1 -1
- package/dist/cjs/chunk/group.js +2 -2
- package/dist/cjs/chunk/group.js.map +1 -1
- package/dist/cjs/chunk/parse.js +63 -67
- package/dist/cjs/chunk/parse.js.map +1 -1
- package/dist/cjs/chunk/parser.d.cts +2 -0
- package/dist/cjs/chunk/parser.d.ts +2 -0
- package/dist/cjs/chunk/parser.js +40 -0
- package/dist/cjs/chunk/parser.js.map +1 -0
- package/dist/cjs/chunk/types.d.cts +2 -2
- package/dist/cjs/chunk/types.d.ts +2 -2
- package/dist/cjs/chunk/version.d.cts +1 -1
- package/dist/cjs/chunk/version.d.ts +1 -1
- package/dist/cjs/chunk/version.js +1 -1
- package/dist/cjs/chunk/version.js.map +1 -1
- package/dist/cjs/features/sections.js +1 -1
- package/dist/cjs/features/sections.js.map +1 -1
- package/dist/cjs/scan/pool.js +1 -1
- package/dist/cjs/scan/pool.js.map +1 -1
- package/dist/cjs/store/batch.d.cts +4 -0
- package/dist/cjs/store/batch.d.ts +4 -0
- package/dist/cjs/store/batch.js +80 -0
- package/dist/cjs/store/batch.js.map +1 -0
- package/dist/cjs/store/duckdb/batch.d.cts +0 -4
- package/dist/cjs/store/duckdb/batch.d.ts +0 -4
- package/dist/cjs/store/duckdb/batch.js +5 -27
- package/dist/cjs/store/duckdb/batch.js.map +1 -1
- package/dist/cjs/store/duckdb/open.d.cts +1 -1
- package/dist/cjs/store/duckdb/open.d.ts +1 -1
- package/dist/cjs/store/duckdb/open.js +1 -1
- package/dist/cjs/store/duckdb/open.js.map +1 -1
- package/dist/cjs/store/sqlite/open.d.cts +1 -1
- package/dist/cjs/store/sqlite/open.d.ts +1 -1
- package/dist/cjs/store/sqlite/open.js +1 -1
- package/dist/cjs/store/sqlite/open.js.map +1 -1
- package/dist/cjs/store/turso/connection.d.cts +3 -0
- package/dist/cjs/store/turso/connection.d.ts +3 -0
- package/dist/cjs/store/turso/connection.js +300 -30
- package/dist/cjs/store/turso/connection.js.map +1 -1
- package/dist/cjs/store/turso/native.d.cts +4 -0
- package/dist/cjs/store/turso/native.d.ts +4 -0
- package/dist/cjs/store/turso/native.js +9 -0
- package/dist/cjs/store/turso/native.js.map +1 -1
- package/dist/cjs/store/turso/open.d.cts +1 -1
- package/dist/cjs/store/turso/open.d.ts +1 -1
- package/dist/cjs/store/turso/open.js +82 -9
- package/dist/cjs/store/turso/open.js.map +1 -1
- package/dist/cjs/store/turso/store.js +103 -1
- package/dist/cjs/store/turso/store.js.map +1 -1
- package/dist/cjs/text/strip.js +1 -3
- package/dist/cjs/text/strip.js.map +1 -1
- package/dist/cjs/workers/parse.js.map +1 -1
- package/dist/esm/chunk/extract.d.ts +3 -3
- package/dist/esm/chunk/extract.js +276 -63
- package/dist/esm/chunk/extract.js.map +1 -1
- package/dist/esm/chunk/group.js +2 -2
- package/dist/esm/chunk/group.js.map +1 -1
- package/dist/esm/chunk/parse.js +65 -64
- package/dist/esm/chunk/parse.js.map +1 -1
- package/dist/esm/chunk/parser.d.ts +2 -0
- package/dist/esm/chunk/parser.js +26 -0
- package/dist/esm/chunk/parser.js.map +1 -0
- package/dist/esm/chunk/types.d.ts +2 -2
- package/dist/esm/chunk/types.js.map +1 -1
- package/dist/esm/chunk/version.d.ts +1 -1
- package/dist/esm/chunk/version.js +1 -1
- package/dist/esm/chunk/version.js.map +1 -1
- package/dist/esm/features/sections.js +1 -1
- package/dist/esm/features/sections.js.map +1 -1
- package/dist/esm/scan/pool.js +1 -1
- package/dist/esm/scan/pool.js.map +1 -1
- package/dist/esm/store/batch.d.ts +4 -0
- package/dist/esm/store/batch.js +21 -0
- package/dist/esm/store/batch.js.map +1 -0
- package/dist/esm/store/duckdb/batch.d.ts +0 -4
- package/dist/esm/store/duckdb/batch.js +2 -19
- package/dist/esm/store/duckdb/batch.js.map +1 -1
- package/dist/esm/store/duckdb/open.d.ts +1 -1
- package/dist/esm/store/duckdb/open.js +1 -1
- package/dist/esm/store/duckdb/open.js.map +1 -1
- package/dist/esm/store/sqlite/open.d.ts +1 -1
- package/dist/esm/store/sqlite/open.js +1 -1
- package/dist/esm/store/sqlite/open.js.map +1 -1
- package/dist/esm/store/turso/connection.d.ts +3 -0
- package/dist/esm/store/turso/connection.js +58 -3
- package/dist/esm/store/turso/connection.js.map +1 -1
- package/dist/esm/store/turso/native.d.ts +4 -0
- package/dist/esm/store/turso/native.js +8 -0
- package/dist/esm/store/turso/native.js.map +1 -1
- package/dist/esm/store/turso/open.d.ts +1 -1
- package/dist/esm/store/turso/open.js +6 -6
- package/dist/esm/store/turso/open.js.map +1 -1
- package/dist/esm/store/turso/store.js +29 -2
- package/dist/esm/store/turso/store.js.map +1 -1
- package/dist/esm/text/strip.js +1 -1
- package/dist/esm/text/strip.js.map +1 -1
- package/dist/esm/workers/parse.js.map +1 -1
- package/package.json +5 -13
- package/skills/sense-setup/SKILL.md +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
1
|
+
import { parser } from './parser.js';
|
|
2
|
+
// markdown-it, like mdast, has no wikilink, embed, or %%comment%% token -- literal text resolved
|
|
3
|
+
// by regex passes over non-code text (code/inline code is placeholder-held and spliced back verbatim).
|
|
3
4
|
const COMMENT_PAIR = /%%[\s\S]*?%%/g;
|
|
4
5
|
// An embed always yields its target, whatever follows a pipe (a resize suffix, never an alias);
|
|
5
6
|
// must run before the wikilink regex below, bang included, or that regex matches first.
|
|
@@ -11,7 +12,7 @@ const BLOCK_ID = / \^[A-Za-z0-9-]+$/gm;
|
|
|
11
12
|
// A callout marker is only ever the first thing in a blockquote's text (Obsidian's grammar),
|
|
12
13
|
// so this anchors to blockquote output alone -- never applied to prose in general.
|
|
13
14
|
const CALLOUT_MARKER = /^\[!\w[\w-]*\][+-]?[ \t]?/;
|
|
14
|
-
// An html
|
|
15
|
+
// An html token's content is raw HTML, block or inline: a full <!-- --> comment is dropped, any
|
|
15
16
|
// remaining tags are stripped, and the text a browser would still render survives.
|
|
16
17
|
const HTML_COMMENT = /<!--[\s\S]*?-->/g;
|
|
17
18
|
const HTML_TAG = /<[^>]+>/g;
|
|
@@ -38,85 +39,297 @@ function embedText(inner) {
|
|
|
38
39
|
function resolveFlavor(text) {
|
|
39
40
|
return text.replace(HTML_COMMENT, '').replace(COMMENT_PAIR, '').replace(EMBED, (_, inner)=>embedText(inner)).replace(WIKILINK, (_, inner)=>wikilinkText(inner)).replace(BLOCK_ID, '');
|
|
40
41
|
}
|
|
41
|
-
// Sibling blocks (list items, blockquote content, footnote bodies) joined one per line;
|
|
42
|
-
// empties dropped so a blank child never widens the gap between its neighbors.
|
|
43
|
-
function joinBlocks(nodes, code) {
|
|
44
|
-
return nodes.map((n)=>extractNode(n, code)).filter((s)=>s.length > 0).join('\n');
|
|
45
|
-
}
|
|
46
|
-
// Inline content (headings, paragraphs, emphasis, links) concatenated with no separator: the
|
|
47
|
-
// source text nodes already carry whatever spacing separates them.
|
|
48
|
-
function joinInline(nodes, code) {
|
|
49
|
-
return nodes.map((n)=>extractNode(n, code)).join('');
|
|
50
|
-
}
|
|
51
42
|
// U+E000 (Private Use Area) never appears in real markdown text, so it is a collision-free
|
|
52
43
|
// placeholder delimiter, spliced back to the literal code value after flavor resolution.
|
|
53
44
|
function codePlaceholder(value, code) {
|
|
54
45
|
const idx = code.push(value) - 1;
|
|
55
46
|
return `\uE000${idx}\uE000`;
|
|
56
47
|
}
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
48
|
+
// GFM's autolink literal ends where its path machine stops: a trail of punctuation is a genuine
|
|
49
|
+
// end (link stops before it) only when trailEndsAt accepts it, else the marks are part of the link.
|
|
50
|
+
const PUNCT = new Set([
|
|
51
|
+
'!',
|
|
52
|
+
'"',
|
|
53
|
+
'&',
|
|
54
|
+
"'",
|
|
55
|
+
')',
|
|
56
|
+
'*',
|
|
57
|
+
',',
|
|
58
|
+
'.',
|
|
59
|
+
';',
|
|
60
|
+
':',
|
|
61
|
+
'<',
|
|
62
|
+
'?',
|
|
63
|
+
'_',
|
|
64
|
+
'~',
|
|
65
|
+
']'
|
|
66
|
+
]);
|
|
67
|
+
const TRAIL_PUNCT = new Set([
|
|
68
|
+
'!',
|
|
69
|
+
'"',
|
|
70
|
+
"'",
|
|
71
|
+
')',
|
|
72
|
+
'*',
|
|
73
|
+
',',
|
|
74
|
+
'.',
|
|
75
|
+
';',
|
|
76
|
+
':',
|
|
77
|
+
'?',
|
|
78
|
+
'_',
|
|
79
|
+
'~'
|
|
80
|
+
]);
|
|
81
|
+
const isAlpha = (c)=>c !== undefined && (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z');
|
|
82
|
+
function trailEndsAt(s, j) {
|
|
83
|
+
for(;;){
|
|
84
|
+
const c = s[j];
|
|
85
|
+
if (c === undefined) return true;
|
|
86
|
+
if (TRAIL_PUNCT.has(c)) {
|
|
87
|
+
j += 1;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (c === '&') {
|
|
91
|
+
j += 1;
|
|
92
|
+
if (!isAlpha(s[j])) return false;
|
|
93
|
+
j += 1;
|
|
94
|
+
while(isAlpha(s[j]))j += 1;
|
|
95
|
+
if (s[j] !== ';') return false;
|
|
96
|
+
j += 1;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (c === ']') {
|
|
100
|
+
j += 1;
|
|
101
|
+
const d = s[j];
|
|
102
|
+
return d === undefined || d === '(' || d === '[' || /\s/u.test(d !== null && d !== void 0 ? d : ' ');
|
|
103
|
+
}
|
|
104
|
+
if (c === '<') return true;
|
|
105
|
+
if (c !== undefined && /^\s$/u.test(c)) return true;
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Where the GFM autolink ends in S (the linkifier's text plus the following text), old-style:
|
|
110
|
+
// punctuation that fails the trail test, or a ) with closes <= opens, extends the link.
|
|
111
|
+
function gfmAutolinkEnd(s) {
|
|
112
|
+
let open = 0;
|
|
113
|
+
let close = 0;
|
|
114
|
+
for(let i = 0; i < s.length; i++){
|
|
115
|
+
const c = s[i];
|
|
116
|
+
if (c === '(') {
|
|
117
|
+
open += 1;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (c === ')' && close < open) {
|
|
121
|
+
close += 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (PUNCT.has(c) && trailEndsAt(s, i)) return i;
|
|
125
|
+
if (c !== undefined && /^\s$/u.test(c)) return i;
|
|
126
|
+
}
|
|
127
|
+
return s.length;
|
|
128
|
+
}
|
|
129
|
+
// linkify-it links more than GFM's autolink literal (bare domains, ftp://, //host); only the
|
|
130
|
+
// GFM forms (http(s)://, www., email) drop their text.
|
|
131
|
+
function isGfmUrl(text) {
|
|
132
|
+
return /^https?:\/\//i.test(text) || /^www\./i.test(text);
|
|
133
|
+
}
|
|
134
|
+
// Only a bare address is GFM's email autolink: a url keeps its @, since a path can hold one.
|
|
135
|
+
function isGfmEmail(text) {
|
|
136
|
+
return !isGfmUrl(text) && text.includes('@');
|
|
137
|
+
}
|
|
138
|
+
// The task-lists plugin injects its checkbox as the item's first inline child.
|
|
139
|
+
function isTaskCheckbox(token) {
|
|
140
|
+
return (token === null || token === void 0 ? void 0 : token.type) === 'html_inline' && /^<input\b/i.test(token.content);
|
|
141
|
+
}
|
|
142
|
+
// A linkify span can swallow the & of a following entity (span ...c& + text amp; z); the
|
|
143
|
+
// re-emitted lead is then a reference the entity rule never saw, so decode it old-style.
|
|
144
|
+
const LEAD_ENTITY = /^&[a-z#][a-z0-9]{1,31};/i;
|
|
145
|
+
// Split a flat token range into its top-level blocks: nesting 1 opens, nesting 0 is one block.
|
|
146
|
+
function topLevelBlocks(tokens) {
|
|
147
|
+
const out = [];
|
|
148
|
+
let i = 0;
|
|
149
|
+
while(i < tokens.length){
|
|
150
|
+
const token = tokens[i];
|
|
151
|
+
if (token.nesting === 1) {
|
|
152
|
+
let depth = 1;
|
|
153
|
+
let j = i + 1;
|
|
154
|
+
while(j < tokens.length && depth > 0){
|
|
155
|
+
depth += tokens[j].nesting === 1 ? 1 : tokens[j].nesting === -1 ? -1 : 0;
|
|
156
|
+
j += 1;
|
|
157
|
+
}
|
|
158
|
+
out.push(tokens.slice(i, j));
|
|
159
|
+
i = j;
|
|
160
|
+
} else if (token.nesting === 0) {
|
|
161
|
+
out.push(tokens.slice(i, i + 1));
|
|
162
|
+
i += 1;
|
|
163
|
+
} else {
|
|
164
|
+
i += 1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
// A first line with no source content (a bare task checkbox, whitespace) contributes no line to
|
|
170
|
+
// the value, so its softbreak is dropped; a first line whose content extraction removes (an
|
|
171
|
+
// autolink, an image) still leaves its line ending behind.
|
|
172
|
+
function paragraphText(children, code) {
|
|
173
|
+
var _children_k;
|
|
174
|
+
let k = 0;
|
|
175
|
+
const first = children[0];
|
|
176
|
+
if (isTaskCheckbox(first)) k = 1;
|
|
177
|
+
while(k < children.length && children[k].type === 'text' && children[k].content.trim() === '')k += 1;
|
|
178
|
+
if (k > 0 && ((_children_k = children[k]) === null || _children_k === void 0 ? void 0 : _children_k.type) === 'softbreak') return inlineText(children.slice(k + 1), code);
|
|
179
|
+
return inlineText(children, code);
|
|
180
|
+
}
|
|
181
|
+
function blockText(tokens, code) {
|
|
182
|
+
const first = tokens[0];
|
|
183
|
+
if (!first) return '';
|
|
184
|
+
switch(first.type){
|
|
185
|
+
case 'heading_open':
|
|
186
|
+
case 'paragraph_open':
|
|
73
187
|
{
|
|
74
|
-
|
|
75
|
-
|
|
188
|
+
var _inline_children;
|
|
189
|
+
const inline = tokens.find((t)=>t.type === 'inline');
|
|
190
|
+
return inline ? paragraphText((_inline_children = inline.children) !== null && _inline_children !== void 0 ? _inline_children : [], code) : '';
|
|
76
191
|
}
|
|
77
|
-
case '
|
|
78
|
-
case '
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
case '
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
case '
|
|
85
|
-
return
|
|
86
|
-
case '
|
|
87
|
-
return
|
|
88
|
-
case '
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
case '
|
|
92
|
-
|
|
93
|
-
return (_node_alt = node.alt) !== null && _node_alt !== void 0 ? _node_alt : '';
|
|
94
|
-
case 'text':
|
|
95
|
-
return node.value;
|
|
96
|
-
case 'inlineCode':
|
|
97
|
-
case 'code':
|
|
98
|
-
return codePlaceholder(node.value, code);
|
|
99
|
-
case 'html':
|
|
100
|
-
return stripHtml(node.value);
|
|
101
|
-
case 'break':
|
|
102
|
-
return ' ';
|
|
192
|
+
case 'fence':
|
|
193
|
+
case 'code_block':
|
|
194
|
+
// Fence and indented code tokens carry the closing newline their lines were joined with.
|
|
195
|
+
return codePlaceholder(first.content.replace(/\n$/, ''), code);
|
|
196
|
+
case 'table_open':
|
|
197
|
+
return tableText(tokens, code);
|
|
198
|
+
case 'ordered_list_open':
|
|
199
|
+
case 'bullet_list_open':
|
|
200
|
+
return listItemsText(tokens, code);
|
|
201
|
+
case 'blockquote_open':
|
|
202
|
+
return blocksIn(tokens.slice(1, -1), code).replace(CALLOUT_MARKER, '');
|
|
203
|
+
case 'html_block':
|
|
204
|
+
// A non-final-line html block carries its last line's terminator, which the mdast value lacked.
|
|
205
|
+
return stripHtml(first.content.replace(/\n$/, ''));
|
|
206
|
+
case 'footnote_reference_open':
|
|
207
|
+
return blocksIn(tokens.slice(1, -1), code);
|
|
103
208
|
default:
|
|
104
209
|
return '';
|
|
105
210
|
}
|
|
106
211
|
}
|
|
212
|
+
// Sibling blocks (list items, blockquote children, footnote bodies) joined one per line;
|
|
213
|
+
// empties dropped so a blank child never widens the gap between its neighbors.
|
|
214
|
+
function blocksIn(tokens, code) {
|
|
215
|
+
return topLevelBlocks(tokens).map((b)=>blockText(b, code)).filter((s)=>s.length > 0).join('\n');
|
|
216
|
+
}
|
|
217
|
+
// The task-lists plugin slices 3 of the marker's 4 chars, leaving its trailing space on the text
|
|
218
|
+
// after the checkbox token. Returns a copy with that space dropped, never a mutation: a block is
|
|
219
|
+
// extracted more than once (parse pre-extracts headings, group.ts extracts again).
|
|
220
|
+
function withoutTaskMarker(item) {
|
|
221
|
+
for(let i = 0; i < item.length; i++){
|
|
222
|
+
var _token_children;
|
|
223
|
+
const token = item[i];
|
|
224
|
+
// The checkbox, if any, sits in the item's first inline only.
|
|
225
|
+
if (token.type !== 'inline' || !((_token_children = token.children) === null || _token_children === void 0 ? void 0 : _token_children.length)) continue;
|
|
226
|
+
const second = token.children[1];
|
|
227
|
+
if (!isTaskCheckbox(token.children[0]) || (second === null || second === void 0 ? void 0 : second.type) !== 'text' || !second.content.startsWith(' ')) return item;
|
|
228
|
+
const children = token.children.slice();
|
|
229
|
+
children[1] = {
|
|
230
|
+
...second,
|
|
231
|
+
content: second.content.slice(1)
|
|
232
|
+
};
|
|
233
|
+
const copy = item.slice();
|
|
234
|
+
copy[i] = {
|
|
235
|
+
...token,
|
|
236
|
+
children
|
|
237
|
+
};
|
|
238
|
+
return copy;
|
|
239
|
+
}
|
|
240
|
+
return item;
|
|
241
|
+
}
|
|
242
|
+
function listItemsText(tokens, code) {
|
|
243
|
+
const items = [];
|
|
244
|
+
for (const item of topLevelBlocks(tokens.slice(1, -1))){
|
|
245
|
+
items.push(blocksIn(withoutTaskMarker(item).slice(1, -1), code));
|
|
246
|
+
}
|
|
247
|
+
return items.filter((s)=>s.length > 0).join('\n');
|
|
248
|
+
}
|
|
249
|
+
// Table rows joined by newline, cells by space (mdast's tableRow/tableCell joiners).
|
|
250
|
+
function tableText(tokens, code) {
|
|
251
|
+
const rows = [];
|
|
252
|
+
let cells = [];
|
|
253
|
+
for (const token of tokens){
|
|
254
|
+
var _token_children;
|
|
255
|
+
if (token.type === 'tr_open') cells = [];
|
|
256
|
+
else if (token.type === 'inline') cells.push(inlineText((_token_children = token.children) !== null && _token_children !== void 0 ? _token_children : [], code));
|
|
257
|
+
else if (token.type === 'tr_close') rows.push(cells.join(' '));
|
|
258
|
+
}
|
|
259
|
+
return rows.join('\n');
|
|
260
|
+
}
|
|
261
|
+
// Inline content (headings, paragraphs, emphasis, links) concatenated with no separator: the
|
|
262
|
+
// source text tokens already carry whatever spacing separates them.
|
|
263
|
+
function inlineText(tokens, code, inAlt = false) {
|
|
264
|
+
let out = '';
|
|
265
|
+
for(let i = 0; i < tokens.length; i++){
|
|
266
|
+
const token = tokens[i];
|
|
267
|
+
if (token.type === 'link_open') {
|
|
268
|
+
let skip = 0;
|
|
269
|
+
let j = i + 1;
|
|
270
|
+
let depth = 1;
|
|
271
|
+
while(j < tokens.length && depth > 0){
|
|
272
|
+
if (tokens[j].type === 'link_open') depth += 1;
|
|
273
|
+
else if (tokens[j].type === 'link_close') depth -= 1;
|
|
274
|
+
j += 1;
|
|
275
|
+
}
|
|
276
|
+
const text = inlineText(tokens.slice(i + 1, j - 1), code, inAlt);
|
|
277
|
+
const markup = token.markup;
|
|
278
|
+
const next = tokens[j];
|
|
279
|
+
if (inAlt) {
|
|
280
|
+
// An image's alt is plain text: mdast kept every link's own text there, autolinks included.
|
|
281
|
+
out += text;
|
|
282
|
+
} else if (markup === 'linkify' && isGfmUrl(text)) {
|
|
283
|
+
// GFM drop: the next text token resumes at the old link's boundary, re-emitting any
|
|
284
|
+
// trimmed trail. <www.x> has no trail to absorb (the brackets bound it), keep the >.
|
|
285
|
+
// Emails keep their whole remainder: GFM's email machine has no trail, so the span
|
|
286
|
+
// already ends at the old boundary and the entity rule has decoded the rest.
|
|
287
|
+
const prev = tokens[i - 1];
|
|
288
|
+
const bracketed = (prev === null || prev === void 0 ? void 0 : prev.type) === 'text' && prev.content.endsWith('<') && (next === null || next === void 0 ? void 0 : next.type) === 'text' && next.content.startsWith('>');
|
|
289
|
+
const tail = (next === null || next === void 0 ? void 0 : next.type) === 'text' ? text + next.content : text;
|
|
290
|
+
const k = bracketed ? text.length : gfmAutolinkEnd(tail);
|
|
291
|
+
out += tail.slice(k).replace(LEAD_ENTITY, (m)=>parser().utils.unescapeAll(m));
|
|
292
|
+
// The trail came out of the next text token, so that token is consumed here, not re-read.
|
|
293
|
+
if ((next === null || next === void 0 ? void 0 : next.type) === 'text') skip = 1;
|
|
294
|
+
} else if (markup === 'linkify' && isGfmEmail(text)) {
|
|
295
|
+
// Email drop: nothing after the span to trim, so the span alone is dropped.
|
|
296
|
+
} else if (markup === 'autolink') {
|
|
297
|
+
// <...> leaf: dropped whole, its brackets live inside the token so nothing follows to trim.
|
|
298
|
+
} else {
|
|
299
|
+
out += text; // user link or non-GFM linkify target: keep the display text
|
|
300
|
+
}
|
|
301
|
+
i = j - 1 + skip;
|
|
302
|
+
} else if (token.type === 'text') {
|
|
303
|
+
out += token.content;
|
|
304
|
+
} else if (token.type === 'softbreak') {
|
|
305
|
+
out += '\n';
|
|
306
|
+
} else if (token.type === 'hardbreak') {
|
|
307
|
+
out += ' ';
|
|
308
|
+
} else if (token.type === 'code_inline') {
|
|
309
|
+
out += codePlaceholder(token.content, code);
|
|
310
|
+
} else if (token.type === 'html_inline') {
|
|
311
|
+
out += stripHtml(token.content);
|
|
312
|
+
} else if (token.type === 'image') {
|
|
313
|
+
var _token_children;
|
|
314
|
+
out += inlineText((_token_children = token.children) !== null && _token_children !== void 0 ? _token_children : [], code, true);
|
|
315
|
+
}
|
|
316
|
+
// Emphasis, strikethrough and footnote markers carry no text of their own.
|
|
317
|
+
}
|
|
318
|
+
return out;
|
|
319
|
+
}
|
|
107
320
|
const CODE_PLACEHOLDER = /\uE000(\d+)\uE000/g;
|
|
108
|
-
// Plain text of one
|
|
109
|
-
//
|
|
110
|
-
export function extractText(
|
|
321
|
+
// Plain text of one parsed block: heading/list/table structure is kept as text, markup (emphasis,
|
|
322
|
+
// link targets, task and callout markers) is dropped. Pure, synchronous.
|
|
323
|
+
export function extractText(block) {
|
|
111
324
|
return extractTexts([
|
|
112
|
-
|
|
325
|
+
block
|
|
113
326
|
]);
|
|
114
327
|
}
|
|
115
328
|
// Blocks resolved together: a blank line inside %%...%% splits it across blocks, and a per-block
|
|
116
329
|
// strip then never sees the closing %%. Code stays placeholder-held across all of them.
|
|
117
|
-
export function extractTexts(
|
|
330
|
+
export function extractTexts(blocks) {
|
|
118
331
|
const code = [];
|
|
119
|
-
const joined =
|
|
332
|
+
const joined = blocks.map((b)=>blockText(b.node, code)).filter((s)=>s.length > 0).join('\n');
|
|
120
333
|
const resolved = resolveFlavor(joined);
|
|
121
334
|
return code.length === 0 ? resolved : resolved.replace(CODE_PLACEHOLDER, (_, i)=>code[Number(i)]);
|
|
122
335
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/chunk/extract.ts"],"sourcesContent":["import type { Nodes } from 'mdast';\n\n// mdast has no wikilink, embed, or %%comment%% node -- literal text resolved by regex passes\n// over non-code text (code/inlineCode is placeholder-held and spliced back verbatim, below).\nconst COMMENT_PAIR = /%%[\\s\\S]*?%%/g;\n// An embed always yields its target, whatever follows a pipe (a resize suffix, never an alias);\n// must run before the wikilink regex below, bang included, or that regex matches first.\nconst EMBED = /!\\[\\[([^\\]]+)\\]\\]/g;\nconst WIKILINK = /\\[\\[([^\\]]+)\\]\\]/g;\n// A trailing \" ^id\" is an Obsidian block anchor, valid only at a line's end; a caret elsewhere\n// (mid-line) is ordinary text and is left alone.\nconst BLOCK_ID = / \\^[A-Za-z0-9-]+$/gm;\n// A callout marker is only ever the first thing in a blockquote's text (Obsidian's grammar),\n// so this anchors to blockquote output alone -- never applied to prose in general.\nconst CALLOUT_MARKER = /^\\[!\\w[\\w-]*\\][+-]?[ \\t]?/;\n// An html node's value is raw HTML, block or inline: a full <!-- --> comment is dropped, any\n// remaining tags are stripped, and the text a browser would still render survives.\nconst HTML_COMMENT = /<!--[\\s\\S]*?-->/g;\nconst HTML_TAG = /<[^>]+>/g;\n\nfunction stripHtml(value: string): string {\n return value.replace(HTML_COMMENT, '').replace(HTML_TAG, '');\n}\n\n// A `#anchor` keeps its text (hash dropped), unlike links.ts's parseWikilinkInner (the resolver\n// authority), which discards it entirely -- replicated here since that return value doesn't fit.\nfunction anchorText(base: string): string {\n const hashIdx = base.indexOf('#');\n if (hashIdx === -1) return base.trim();\n const target = base.slice(0, hashIdx).trim();\n const anchor = base.slice(hashIdx + 1).trim();\n return target ? `${target} ${anchor}` : anchor;\n}\n\nfunction wikilinkText(inner: string): string {\n const pipeIdx = inner.indexOf('|');\n return pipeIdx === -1 ? anchorText(inner) : inner.slice(pipeIdx + 1).trim();\n}\n\nfunction embedText(inner: string): string {\n const pipeIdx = inner.indexOf('|');\n return anchorText(pipeIdx === -1 ? inner : inner.slice(0, pipeIdx));\n}\n\nfunction resolveFlavor(text: string): string {\n return text\n .replace(HTML_COMMENT, '')\n .replace(COMMENT_PAIR, '')\n .replace(EMBED, (_, inner: string) => embedText(inner))\n .replace(WIKILINK, (_, inner: string) => wikilinkText(inner))\n .replace(BLOCK_ID, '');\n}\n\n// Sibling blocks (list items, blockquote content, footnote bodies) joined one per line;\n// empties dropped so a blank child never widens the gap between its neighbors.\nfunction joinBlocks(nodes: Nodes[], code: string[]): string {\n return nodes\n .map((n) => extractNode(n, code))\n .filter((s) => s.length > 0)\n .join('\\n');\n}\n\n// Inline content (headings, paragraphs, emphasis, links) concatenated with no separator: the\n// source text nodes already carry whatever spacing separates them.\nfunction joinInline(nodes: Nodes[], code: string[]): string {\n return nodes.map((n) => extractNode(n, code)).join('');\n}\n\n// U+E000 (Private Use Area) never appears in real markdown text, so it is a collision-free\n// placeholder delimiter, spliced back to the literal code value after flavor resolution.\nfunction codePlaceholder(value: string, code: string[]): string {\n const idx = code.push(value) - 1;\n return `\\uE000${idx}\\uE000`;\n}\n\n// A GFM autolink's display text is its own target, missing only the scheme\n// mdast-util-gfm-autolink-literal fills into node.url (http:// for www., mailto: for an email).\nconst SCHEME_PREFIX = /^[a-z][a-z0-9+.-]*:\\/{0,2}/i;\nfunction isAutolink(url: string, text: string): boolean {\n return url === text || url.replace(SCHEME_PREFIX, '') === text;\n}\n\nfunction extractNode(node: Nodes, code: string[]): string {\n switch (node.type) {\n case 'root':\n case 'list':\n case 'listItem':\n case 'footnoteDefinition':\n return joinBlocks(node.children, code);\n case 'blockquote':\n return joinBlocks(node.children, code).replace(CALLOUT_MARKER, '');\n case 'link': {\n const text = joinInline(node.children, code);\n return isAutolink(node.url, text) ? '' : text;\n }\n case 'heading':\n case 'paragraph':\n case 'linkReference':\n case 'emphasis':\n case 'strong':\n case 'delete':\n return joinInline(node.children, code);\n case 'table':\n return node.children.map((n) => extractNode(n, code)).join('\\n');\n case 'tableRow':\n return node.children.map((n) => extractNode(n, code)).join(' ');\n case 'tableCell':\n return joinInline(node.children, code);\n case 'image':\n case 'imageReference':\n return node.alt ?? '';\n case 'text':\n return node.value;\n case 'inlineCode':\n case 'code':\n return codePlaceholder(node.value, code);\n case 'html':\n return stripHtml(node.value);\n case 'break':\n return ' ';\n default:\n return '';\n }\n}\n\nconst CODE_PLACEHOLDER = /\\uE000(\\d+)\\uE000/g;\n\n// Plain text of one mdast node (or a whole tree): heading/list/table structure is kept as text,\n// markup (emphasis, link targets, task and callout markers) is dropped. Pure, synchronous.\nexport function extractText(node: Nodes): string {\n return extractTexts([node]);\n}\n\n// Blocks resolved together: a blank line inside %%...%% splits it across blocks, and a per-block\n// strip then never sees the closing %%. Code stays placeholder-held across all of them.\nexport function extractTexts(nodes: Nodes[]): string {\n const code: string[] = [];\n const joined = nodes\n .map((n) => extractNode(n, code))\n .filter((s) => s.length > 0)\n .join('\\n');\n const resolved = resolveFlavor(joined);\n return code.length === 0 ? resolved : resolved.replace(CODE_PLACEHOLDER, (_, i: string) => code[Number(i)]);\n}\n"],"names":["COMMENT_PAIR","EMBED","WIKILINK","BLOCK_ID","CALLOUT_MARKER","HTML_COMMENT","HTML_TAG","stripHtml","value","replace","anchorText","base","hashIdx","indexOf","trim","target","slice","anchor","wikilinkText","inner","pipeIdx","embedText","resolveFlavor","text","_","joinBlocks","nodes","code","map","n","extractNode","filter","s","length","join","joinInline","codePlaceholder","idx","push","SCHEME_PREFIX","isAutolink","url","node","type","children","alt","CODE_PLACEHOLDER","extractText","extractTexts","joined","resolved","i","Number"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/chunk/extract.ts"],"sourcesContent":["import type { Token } from 'markdown-it';\nimport { parser } from './parser.ts';\nimport type { Block } from './types.ts';\n\n// markdown-it, like mdast, has no wikilink, embed, or %%comment%% token -- literal text resolved\n// by regex passes over non-code text (code/inline code is placeholder-held and spliced back verbatim).\nconst COMMENT_PAIR = /%%[\\s\\S]*?%%/g;\n// An embed always yields its target, whatever follows a pipe (a resize suffix, never an alias);\n// must run before the wikilink regex below, bang included, or that regex matches first.\nconst EMBED = /!\\[\\[([^\\]]+)\\]\\]/g;\nconst WIKILINK = /\\[\\[([^\\]]+)\\]\\]/g;\n// A trailing \" ^id\" is an Obsidian block anchor, valid only at a line's end; a caret elsewhere\n// (mid-line) is ordinary text and is left alone.\nconst BLOCK_ID = / \\^[A-Za-z0-9-]+$/gm;\n// A callout marker is only ever the first thing in a blockquote's text (Obsidian's grammar),\n// so this anchors to blockquote output alone -- never applied to prose in general.\nconst CALLOUT_MARKER = /^\\[!\\w[\\w-]*\\][+-]?[ \\t]?/;\n// An html token's content is raw HTML, block or inline: a full <!-- --> comment is dropped, any\n// remaining tags are stripped, and the text a browser would still render survives.\nconst HTML_COMMENT = /<!--[\\s\\S]*?-->/g;\nconst HTML_TAG = /<[^>]+>/g;\n\nfunction stripHtml(value: string): string {\n return value.replace(HTML_COMMENT, '').replace(HTML_TAG, '');\n}\n\n// A `#anchor` keeps its text (hash dropped), unlike links.ts's parseWikilinkInner (the resolver\n// authority), which discards it entirely -- replicated here since that return value doesn't fit.\nfunction anchorText(base: string): string {\n const hashIdx = base.indexOf('#');\n if (hashIdx === -1) return base.trim();\n const target = base.slice(0, hashIdx).trim();\n const anchor = base.slice(hashIdx + 1).trim();\n return target ? `${target} ${anchor}` : anchor;\n}\n\nfunction wikilinkText(inner: string): string {\n const pipeIdx = inner.indexOf('|');\n return pipeIdx === -1 ? anchorText(inner) : inner.slice(pipeIdx + 1).trim();\n}\n\nfunction embedText(inner: string): string {\n const pipeIdx = inner.indexOf('|');\n return anchorText(pipeIdx === -1 ? inner : inner.slice(0, pipeIdx));\n}\n\nfunction resolveFlavor(text: string): string {\n return text\n .replace(HTML_COMMENT, '')\n .replace(COMMENT_PAIR, '')\n .replace(EMBED, (_, inner: string) => embedText(inner))\n .replace(WIKILINK, (_, inner: string) => wikilinkText(inner))\n .replace(BLOCK_ID, '');\n}\n\n// U+E000 (Private Use Area) never appears in real markdown text, so it is a collision-free\n// placeholder delimiter, spliced back to the literal code value after flavor resolution.\nfunction codePlaceholder(value: string, code: string[]): string {\n const idx = code.push(value) - 1;\n return `\\uE000${idx}\\uE000`;\n}\n\n// GFM's autolink literal ends where its path machine stops: a trail of punctuation is a genuine\n// end (link stops before it) only when trailEndsAt accepts it, else the marks are part of the link.\nconst PUNCT = new Set(['!', '\"', '&', \"'\", ')', '*', ',', '.', ';', ':', '<', '?', '_', '~', ']']);\nconst TRAIL_PUNCT = new Set(['!', '\"', \"'\", ')', '*', ',', '.', ';', ':', '?', '_', '~']);\nconst isAlpha = (c: string | undefined): boolean => c !== undefined && ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));\nfunction trailEndsAt(s: string, j: number): boolean {\n for (;;) {\n const c = s[j];\n if (c === undefined) return true;\n if (TRAIL_PUNCT.has(c)) {\n j += 1;\n continue;\n }\n if (c === '&') {\n j += 1;\n if (!isAlpha(s[j])) return false;\n j += 1;\n while (isAlpha(s[j])) j += 1;\n if (s[j] !== ';') return false;\n j += 1;\n continue;\n }\n if (c === ']') {\n j += 1;\n const d = s[j];\n return d === undefined || d === '(' || d === '[' || /\\s/u.test(d ?? ' ');\n }\n if (c === '<') return true;\n if (c !== undefined && /^\\s$/u.test(c)) return true;\n return false;\n }\n}\n// Where the GFM autolink ends in S (the linkifier's text plus the following text), old-style:\n// punctuation that fails the trail test, or a ) with closes <= opens, extends the link.\nfunction gfmAutolinkEnd(s: string): number {\n let open = 0;\n let close = 0;\n for (let i = 0; i < s.length; i++) {\n const c = s[i];\n if (c === '(') {\n open += 1;\n continue;\n }\n if (c === ')' && close < open) {\n close += 1;\n continue;\n }\n if (PUNCT.has(c) && trailEndsAt(s, i)) return i;\n if (c !== undefined && /^\\s$/u.test(c)) return i;\n }\n return s.length;\n}\n// linkify-it links more than GFM's autolink literal (bare domains, ftp://, //host); only the\n// GFM forms (http(s)://, www., email) drop their text.\nfunction isGfmUrl(text: string): boolean {\n return /^https?:\\/\\//i.test(text) || /^www\\./i.test(text);\n}\n// Only a bare address is GFM's email autolink: a url keeps its @, since a path can hold one.\nfunction isGfmEmail(text: string): boolean {\n return !isGfmUrl(text) && text.includes('@');\n}\n// The task-lists plugin injects its checkbox as the item's first inline child.\nfunction isTaskCheckbox(token: Token | undefined): boolean {\n return token?.type === 'html_inline' && /^<input\\b/i.test(token.content);\n}\n// A linkify span can swallow the & of a following entity (span ...c& + text amp; z); the\n// re-emitted lead is then a reference the entity rule never saw, so decode it old-style.\nconst LEAD_ENTITY = /^&[a-z#][a-z0-9]{1,31};/i;\n\n// Split a flat token range into its top-level blocks: nesting 1 opens, nesting 0 is one block.\nfunction topLevelBlocks(tokens: Token[]): Token[][] {\n const out: Token[][] = [];\n let i = 0;\n while (i < tokens.length) {\n const token = tokens[i];\n if (token.nesting === 1) {\n let depth = 1;\n let j = i + 1;\n while (j < tokens.length && depth > 0) {\n depth += tokens[j].nesting === 1 ? 1 : tokens[j].nesting === -1 ? -1 : 0;\n j += 1;\n }\n out.push(tokens.slice(i, j));\n i = j;\n } else if (token.nesting === 0) {\n out.push(tokens.slice(i, i + 1));\n i += 1;\n } else {\n i += 1;\n }\n }\n return out;\n}\n\n// A first line with no source content (a bare task checkbox, whitespace) contributes no line to\n// the value, so its softbreak is dropped; a first line whose content extraction removes (an\n// autolink, an image) still leaves its line ending behind.\nfunction paragraphText(children: Token[], code: string[]): string {\n let k = 0;\n const first = children[0];\n if (isTaskCheckbox(first)) k = 1;\n while (k < children.length && children[k].type === 'text' && children[k].content.trim() === '') k += 1;\n if (k > 0 && children[k]?.type === 'softbreak') return inlineText(children.slice(k + 1), code);\n return inlineText(children, code);\n}\n\nfunction blockText(tokens: Token[], code: string[]): string {\n const first = tokens[0];\n if (!first) return '';\n switch (first.type) {\n case 'heading_open':\n case 'paragraph_open': {\n const inline = tokens.find((t) => t.type === 'inline');\n return inline ? paragraphText(inline.children ?? [], code) : '';\n }\n case 'fence':\n case 'code_block':\n // Fence and indented code tokens carry the closing newline their lines were joined with.\n return codePlaceholder(first.content.replace(/\\n$/, ''), code);\n case 'table_open':\n return tableText(tokens, code);\n case 'ordered_list_open':\n case 'bullet_list_open':\n return listItemsText(tokens, code);\n case 'blockquote_open':\n return blocksIn(tokens.slice(1, -1), code).replace(CALLOUT_MARKER, '');\n case 'html_block':\n // A non-final-line html block carries its last line's terminator, which the mdast value lacked.\n return stripHtml(first.content.replace(/\\n$/, ''));\n case 'footnote_reference_open':\n return blocksIn(tokens.slice(1, -1), code);\n default:\n return '';\n }\n}\n\n// Sibling blocks (list items, blockquote children, footnote bodies) joined one per line;\n// empties dropped so a blank child never widens the gap between its neighbors.\nfunction blocksIn(tokens: Token[], code: string[]): string {\n return topLevelBlocks(tokens)\n .map((b) => blockText(b, code))\n .filter((s) => s.length > 0)\n .join('\\n');\n}\n\n// The task-lists plugin slices 3 of the marker's 4 chars, leaving its trailing space on the text\n// after the checkbox token. Returns a copy with that space dropped, never a mutation: a block is\n// extracted more than once (parse pre-extracts headings, group.ts extracts again).\nfunction withoutTaskMarker(item: Token[]): Token[] {\n for (let i = 0; i < item.length; i++) {\n const token = item[i];\n // The checkbox, if any, sits in the item's first inline only.\n if (token.type !== 'inline' || !token.children?.length) continue;\n const second = token.children[1];\n if (!isTaskCheckbox(token.children[0]) || second?.type !== 'text' || !second.content.startsWith(' ')) return item;\n const children = token.children.slice();\n children[1] = { ...second, content: second.content.slice(1) } as Token;\n const copy = item.slice();\n copy[i] = { ...token, children } as Token;\n return copy;\n }\n return item;\n}\n\nfunction listItemsText(tokens: Token[], code: string[]): string {\n const items: string[] = [];\n for (const item of topLevelBlocks(tokens.slice(1, -1))) {\n items.push(blocksIn(withoutTaskMarker(item).slice(1, -1), code));\n }\n return items.filter((s) => s.length > 0).join('\\n');\n}\n\n// Table rows joined by newline, cells by space (mdast's tableRow/tableCell joiners).\nfunction tableText(tokens: Token[], code: string[]): string {\n const rows: string[] = [];\n let cells: string[] = [];\n for (const token of tokens) {\n if (token.type === 'tr_open') cells = [];\n else if (token.type === 'inline') cells.push(inlineText(token.children ?? [], code));\n else if (token.type === 'tr_close') rows.push(cells.join(' '));\n }\n return rows.join('\\n');\n}\n\n// Inline content (headings, paragraphs, emphasis, links) concatenated with no separator: the\n// source text tokens already carry whatever spacing separates them.\nfunction inlineText(tokens: Token[], code: string[], inAlt = false): string {\n let out = '';\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n if (token.type === 'link_open') {\n let skip = 0;\n let j = i + 1;\n let depth = 1;\n while (j < tokens.length && depth > 0) {\n if (tokens[j].type === 'link_open') depth += 1;\n else if (tokens[j].type === 'link_close') depth -= 1;\n j += 1;\n }\n const text = inlineText(tokens.slice(i + 1, j - 1), code, inAlt);\n const markup = token.markup;\n const next = tokens[j];\n if (inAlt) {\n // An image's alt is plain text: mdast kept every link's own text there, autolinks included.\n out += text;\n } else if (markup === 'linkify' && isGfmUrl(text)) {\n // GFM drop: the next text token resumes at the old link's boundary, re-emitting any\n // trimmed trail. <www.x> has no trail to absorb (the brackets bound it), keep the >.\n // Emails keep their whole remainder: GFM's email machine has no trail, so the span\n // already ends at the old boundary and the entity rule has decoded the rest.\n const prev = tokens[i - 1];\n const bracketed = prev?.type === 'text' && prev.content.endsWith('<') && next?.type === 'text' && next.content.startsWith('>');\n const tail = next?.type === 'text' ? text + next.content : text;\n const k = bracketed ? text.length : gfmAutolinkEnd(tail);\n out += tail.slice(k).replace(LEAD_ENTITY, (m) => parser().utils.unescapeAll(m));\n // The trail came out of the next text token, so that token is consumed here, not re-read.\n if (next?.type === 'text') skip = 1;\n } else if (markup === 'linkify' && isGfmEmail(text)) {\n // Email drop: nothing after the span to trim, so the span alone is dropped.\n } else if (markup === 'autolink') {\n // <...> leaf: dropped whole, its brackets live inside the token so nothing follows to trim.\n } else {\n out += text; // user link or non-GFM linkify target: keep the display text\n }\n i = j - 1 + skip;\n } else if (token.type === 'text') {\n out += token.content;\n } else if (token.type === 'softbreak') {\n out += '\\n';\n } else if (token.type === 'hardbreak') {\n out += ' ';\n } else if (token.type === 'code_inline') {\n out += codePlaceholder(token.content, code);\n } else if (token.type === 'html_inline') {\n out += stripHtml(token.content);\n } else if (token.type === 'image') {\n out += inlineText(token.children ?? [], code, true);\n }\n // Emphasis, strikethrough and footnote markers carry no text of their own.\n }\n return out;\n}\n\nconst CODE_PLACEHOLDER = /\\uE000(\\d+)\\uE000/g;\n\n// Plain text of one parsed block: heading/list/table structure is kept as text, markup (emphasis,\n// link targets, task and callout markers) is dropped. Pure, synchronous.\nexport function extractText(block: Block): string {\n return extractTexts([block]);\n}\n\n// Blocks resolved together: a blank line inside %%...%% splits it across blocks, and a per-block\n// strip then never sees the closing %%. Code stays placeholder-held across all of them.\nexport function extractTexts(blocks: Block[]): string {\n const code: string[] = [];\n const joined = blocks\n .map((b) => blockText(b.node, code))\n .filter((s) => s.length > 0)\n .join('\\n');\n const resolved = resolveFlavor(joined);\n return code.length === 0 ? resolved : resolved.replace(CODE_PLACEHOLDER, (_, i: string) => code[Number(i)]);\n}\n"],"names":["parser","COMMENT_PAIR","EMBED","WIKILINK","BLOCK_ID","CALLOUT_MARKER","HTML_COMMENT","HTML_TAG","stripHtml","value","replace","anchorText","base","hashIdx","indexOf","trim","target","slice","anchor","wikilinkText","inner","pipeIdx","embedText","resolveFlavor","text","_","codePlaceholder","code","idx","push","PUNCT","Set","TRAIL_PUNCT","isAlpha","c","undefined","trailEndsAt","s","j","has","d","test","gfmAutolinkEnd","open","close","i","length","isGfmUrl","isGfmEmail","includes","isTaskCheckbox","token","type","content","LEAD_ENTITY","topLevelBlocks","tokens","out","nesting","depth","paragraphText","children","k","first","inlineText","blockText","inline","find","t","tableText","listItemsText","blocksIn","map","b","filter","join","withoutTaskMarker","item","second","startsWith","copy","items","rows","cells","inAlt","skip","markup","next","prev","bracketed","endsWith","tail","m","utils","unescapeAll","CODE_PLACEHOLDER","extractText","block","extractTexts","blocks","joined","node","resolved","Number"],"mappings":"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"}
|
package/dist/esm/chunk/group.js
CHANGED
|
@@ -105,7 +105,7 @@ const ATOMIC_TYPES = new Set([
|
|
|
105
105
|
// Atomic (code/table/list) pieces are always a raw line slice: re-parsing a table's later pieces
|
|
106
106
|
// without their header/delimiter rows would demote them to paragraph text.
|
|
107
107
|
function piece(pieceLines, startLine, endLine, blockType, textMode) {
|
|
108
|
-
const text = ATOMIC_TYPES.has(blockType) || textMode === 'raw' ? pieceLines.join('\n') : parse(pieceLines.join('\n')).map((b)=>extractText(b
|
|
108
|
+
const text = ATOMIC_TYPES.has(blockType) || textMode === 'raw' ? pieceLines.join('\n') : parse(pieceLines.join('\n')).map((b)=>extractText(b)).join('\n');
|
|
109
109
|
return {
|
|
110
110
|
startLine,
|
|
111
111
|
endLine,
|
|
@@ -191,7 +191,7 @@ function groupScope(scopeBlocks, lines, resolved) {
|
|
|
191
191
|
}
|
|
192
192
|
continue;
|
|
193
193
|
}
|
|
194
|
-
const extracted = extractText(block
|
|
194
|
+
const extracted = extractText(block);
|
|
195
195
|
const sizeText = resolved.text === 'raw' ? raw : extracted;
|
|
196
196
|
if (block.type === 'heading') {
|
|
197
197
|
addPart(extracted, sizeText, block.startLine, block.endLine);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/chunk/group.ts"],"sourcesContent":["import { extractText } from './extract.ts';\nimport { parse } from './parse.ts';\nimport { DEFAULT_TARGET_TOKENS, estimateTokens } from './tokens.ts';\nimport type { Block, BlockType, Chunk, ChunkOptions } from './types.ts';\n\nconst PGC_GROUP_SIZE = 2;\nconst OVERSIZE_TRIGGER_MULTIPLE = 2;\n\ninterface ResolvedOptions {\n targetTokens: number;\n text: 'extracted' | 'raw';\n}\n\nfunction resolveOptions(opts?: ChunkOptions): ResolvedOptions {\n return {\n targetTokens: opts?.targetTokens ?? DEFAULT_TARGET_TOKENS,\n text: opts?.text ?? 'raw',\n };\n}\n\n// A heading of any depth ends the current scope and starts a new one (D1); the heading block\n// itself is carried into the new scope, where it joins that scope's first group (F7/F10).\nfunction splitScopes(blocks: Block[]): Block[][] {\n const scopes: Block[][] = [];\n let current: Block[] = [];\n for (const block of blocks) {\n if (block.type === 'heading' && current.length > 0) {\n scopes.push(current);\n current = [];\n }\n current.push(block);\n }\n if (current.length > 0) scopes.push(current);\n return scopes;\n}\n\ninterface Part {\n startLine: number;\n endLine: number;\n text: string;\n // True for a sub-line split piece: its text is already the final slice, not the whole line --\n // group()'s extent-based raw re-slice must not touch it (siblings share startLine === endLine).\n final?: boolean;\n}\n\nfunction finalize(parts: Part[]): (Chunk & { final?: boolean }) | undefined {\n if (parts.length === 0) return undefined;\n const first = parts[0];\n const last = parts[parts.length - 1];\n return { startLine: first.startLine, endLine: last.endLine, text: parts.map((p) => p.text).join('\\n'), final: parts.some((p) => p.final) };\n}\n\nconst NEWLINE_TOKENS = estimateTokens('\\n');\n// Built on first use and kept: each construction is ~3.5 ms, and only an oversize block is ever\n// split, so no command pays for a segmenter it never reaches.\nconst SEGMENTERS = new Map<string, Intl.Segmenter>();\n\nfunction segmentsOf(text: string, granularity: 'sentence' | 'word'): string[] {\n let segmenter = SEGMENTERS.get(granularity);\n if (!segmenter) {\n segmenter = new Intl.Segmenter(undefined, { granularity });\n SEGMENTERS.set(granularity, segmenter);\n }\n return Array.from(segmenter.segment(text), (s) => s.segment);\n}\n\n// Greedily packs segments (already contiguous, tiling the source text with no gaps) into groups\n// of at most `working` estimated tokens; a lone segment over `working` still stands alone.\nfunction pack(segments: string[], working: number): string[] {\n const groups: string[] = [];\n let current = '';\n let tokens = 0;\n for (const segment of segments) {\n const segmentTokens = estimateTokens(segment);\n if (current.length > 0 && tokens + segmentTokens > working) {\n groups.push(current);\n current = '';\n tokens = 0;\n }\n current += segment;\n tokens += segmentTokens;\n }\n if (current.length > 0) groups.push(current);\n return groups;\n}\n\n// Line-split alone can't shrink a lone dense line (the CJK case): falls back to sentence then\n// word boundaries (Intl.Segmenter, the same grapheme-safe engine as segment.ts), mode-agnostic on `text`.\nfunction splitLineText(text: string, working: number): string[] {\n const sentences = segmentsOf(text, 'sentence');\n const out: string[] = [];\n let current = '';\n let tokens = 0;\n const flush = () => {\n if (current.length > 0) {\n out.push(current);\n current = '';\n tokens = 0;\n }\n };\n for (const sentence of sentences) {\n const sentenceTokens = estimateTokens(sentence);\n if (sentenceTokens > working) {\n flush();\n out.push(...pack(segmentsOf(sentence, 'word'), working));\n continue;\n }\n if (current.length > 0 && tokens + sentenceTokens > working) flush();\n current += sentence;\n tokens += sentenceTokens;\n }\n flush();\n return out;\n}\n\nconst ATOMIC_TYPES: ReadonlySet<BlockType> = new Set(['code', 'table', 'list']);\n\n// Atomic (code/table/list) pieces are always a raw line slice: re-parsing a table's later pieces\n// without their header/delimiter rows would demote them to paragraph text.\nfunction piece(pieceLines: string[], startLine: number, endLine: number, blockType: BlockType, textMode: 'extracted' | 'raw'): Part {\n const text =\n ATOMIC_TYPES.has(blockType) || textMode === 'raw'\n ? pieceLines.join('\\n')\n : parse(pieceLines.join('\\n'))\n .map((b) => extractText(b.node))\n .join('\\n');\n return { startLine, endLine, text };\n}\n\n// A one-line piece over working can't shrink via another line-boundary pass (rule 5's gap), so it\n// splits at sentence/word boundaries instead; `final` stops group() re-deriving its text by extent (F5).\nfunction finalizePiece(pieceLines: string[], startLine: number, endLine: number, working: number, blockType: BlockType, textMode: 'extracted' | 'raw'): Part[] {\n const p = piece(pieceLines, startLine, endLine, blockType, textMode);\n if (pieceLines.length === 1 && estimateTokens(p.text) > working) {\n return splitLineText(p.text, working).map((text) => ({ startLine, endLine, text, final: true }));\n }\n return [p];\n}\n\n// A block over 2x working size splits at line boundaries into pieces each <= working size, never\n// mid-line. `seed`: pending tokens (e.g. a heading) the first piece must join, checked against the limit.\nfunction splitOversizeBlock(lines: string[], startLine: number, endLine: number, working: number, blockType: BlockType, textMode: 'extracted' | 'raw', seed = 0): Part[] {\n const pieces: Part[] = [];\n let pieceLines: string[] = [];\n let pieceStart = startLine;\n let tokens = seed;\n for (let line = startLine; line <= endLine; line++) {\n const lineText = lines[line - 1];\n const sep = pieceLines.length > 0 || tokens > 0 ? NEWLINE_TOKENS : 0;\n const lineTokens = estimateTokens(lineText);\n if (pieceLines.length > 0 && tokens + sep + lineTokens > working) {\n pieces.push(...finalizePiece(pieceLines, pieceStart, line - 1, working, blockType, textMode));\n pieceLines = [];\n tokens = 0;\n pieceStart = line;\n pieceLines.push(lineText);\n tokens += lineTokens;\n continue;\n }\n pieceLines.push(lineText);\n tokens += sep + lineTokens;\n }\n if (pieceLines.length > 0) pieces.push(...finalizePiece(pieceLines, pieceStart, endLine, working, blockType, textMode));\n return pieces;\n}\n\n// One heading scope's groups (D1): a heading opens the first group, and an oversize block\n// (rule 5, including an oversize heading) splits into pieces that each close their own group.\nfunction groupScope(scopeBlocks: Block[], lines: string[], resolved: ResolvedOptions): (Chunk & { final?: boolean })[] {\n const working = resolved.targetTokens;\n const trigger = working * OVERSIZE_TRIGGER_MULTIPLE;\n const finished: (Chunk & { final?: boolean })[] = [];\n let parts: Part[] = [];\n let paragraphCount = 0;\n let tokens = 0;\n\n function close(): void {\n const group = finalize(parts);\n if (group) finished.push(group);\n parts = [];\n paragraphCount = 0;\n tokens = 0;\n }\n\n // tokens tracks the active text mode's own estimate (a newline between parts costs\n // NEWLINE_TOKENS too), so packing decisions size the text the chunk will actually ship as.\n function addPart(text: string, sizeText: string, startLine: number, endLine: number): void {\n tokens += (parts.length > 0 ? NEWLINE_TOKENS : 0) + estimateTokens(sizeText);\n parts.push({ startLine, endLine, text });\n }\n\n for (const block of scopeBlocks) {\n const raw = lines.slice(block.startLine - 1, block.endLine).join('\\n');\n const blockTokens = estimateTokens(raw);\n\n if (blockTokens > trigger) {\n const seed = parts.length > 0 ? tokens : 0;\n for (const p of splitOversizeBlock(lines, block.startLine, block.endLine, working, block.type, resolved.text, seed)) {\n parts.push(p);\n close();\n }\n continue;\n }\n\n const extracted = extractText(block.node);\n const sizeText = resolved.text === 'raw' ? raw : extracted;\n\n if (block.type === 'heading') {\n addPart(extracted, sizeText, block.startLine, block.endLine);\n continue;\n }\n\n // The 2x-working invariant holds even under pgc's paper-faithful 2-paragraph pairing --\n // close first if the pair about to form would cross it.\n const pairOversize = parts.length > 0 && tokens + NEWLINE_TOKENS + blockTokens > trigger;\n if (pairOversize) close();\n\n addPart(extracted, sizeText, block.startLine, block.endLine);\n paragraphCount++;\n\n if (paragraphCount >= PGC_GROUP_SIZE) close();\n }\n close();\n\n return finished;\n}\n\n// Groups already-parsed blocks per opts (D1/D3), against the same body the blocks were parsed\n// from (line lookups for oversize splitting).\nexport function group(blocks: Block[], body: string, opts?: ChunkOptions): Chunk[] {\n const resolved = resolveOptions(opts);\n const lines = body.split('\\n');\n const chunks: (Chunk & { final?: boolean })[] = [];\n for (const scope of splitScopes(blocks)) chunks.push(...groupScope(scope, lines, resolved));\n // 'raw': the chunk's own source lines verbatim, replacing the flavor-resolved join above (D9).\n // A `final` chunk already carries its own slice's raw text; re-slicing by extent would return the whole shared line.\n const texted =\n resolved.text === 'raw'\n ? chunks.map((c) =>\n c.final\n ? c\n : {\n ...c,\n text: lines\n .slice(c.startLine - 1, c.endLine)\n .join('\\n')\n .trim(),\n }\n )\n : chunks;\n // A group can be all-blank (flavor-stripped to nothing, or a raw slice of pure syntax); it never produces a chunk.\n return texted.filter((c) => c.text.trim().length > 0).map((c) => ({ startLine: c.startLine, endLine: c.endLine, text: c.text }));\n}\n"],"names":["extractText","parse","DEFAULT_TARGET_TOKENS","estimateTokens","PGC_GROUP_SIZE","OVERSIZE_TRIGGER_MULTIPLE","resolveOptions","opts","targetTokens","text","splitScopes","blocks","scopes","current","block","type","length","push","finalize","parts","undefined","first","last","startLine","endLine","map","p","join","final","some","NEWLINE_TOKENS","SEGMENTERS","Map","segmentsOf","granularity","segmenter","get","Intl","Segmenter","set","Array","from","segment","s","pack","segments","working","groups","tokens","segmentTokens","splitLineText","sentences","out","flush","sentence","sentenceTokens","ATOMIC_TYPES","Set","piece","pieceLines","blockType","textMode","has","b","node","finalizePiece","splitOversizeBlock","lines","seed","pieces","pieceStart","line","lineText","sep","lineTokens","groupScope","scopeBlocks","resolved","trigger","finished","paragraphCount","close","group","addPart","sizeText","raw","slice","blockTokens","extracted","pairOversize","body","split","chunks","scope","texted","c","trim","filter"],"mappings":"AAAA,SAASA,WAAW,QAAQ,eAAe;AAC3C,SAASC,KAAK,QAAQ,aAAa;AACnC,SAASC,qBAAqB,EAAEC,cAAc,QAAQ,cAAc;AAGpE,MAAMC,iBAAiB;AACvB,MAAMC,4BAA4B;AAOlC,SAASC,eAAeC,IAAmB;;IACzC,OAAO;QACLC,YAAY,UAAED,iBAAAA,2BAAAA,KAAMC,YAAY,uCAAIN;QACpCO,IAAI,WAAEF,iBAAAA,2BAAAA,KAAME,IAAI,yCAAI;IACtB;AACF;AAEA,6FAA6F;AAC7F,0FAA0F;AAC1F,SAASC,YAAYC,MAAe;IAClC,MAAMC,SAAoB,EAAE;IAC5B,IAAIC,UAAmB,EAAE;IACzB,KAAK,MAAMC,SAASH,OAAQ;QAC1B,IAAIG,MAAMC,IAAI,KAAK,aAAaF,QAAQG,MAAM,GAAG,GAAG;YAClDJ,OAAOK,IAAI,CAACJ;YACZA,UAAU,EAAE;QACd;QACAA,QAAQI,IAAI,CAACH;IACf;IACA,IAAID,QAAQG,MAAM,GAAG,GAAGJ,OAAOK,IAAI,CAACJ;IACpC,OAAOD;AACT;AAWA,SAASM,SAASC,KAAa;IAC7B,IAAIA,MAAMH,MAAM,KAAK,GAAG,OAAOI;IAC/B,MAAMC,QAAQF,KAAK,CAAC,EAAE;IACtB,MAAMG,OAAOH,KAAK,CAACA,MAAMH,MAAM,GAAG,EAAE;IACpC,OAAO;QAAEO,WAAWF,MAAME,SAAS;QAAEC,SAASF,KAAKE,OAAO;QAAEf,MAAMU,MAAMM,GAAG,CAAC,CAACC,IAAMA,EAAEjB,IAAI,EAAEkB,IAAI,CAAC;QAAOC,OAAOT,MAAMU,IAAI,CAAC,CAACH,IAAMA,EAAEE,KAAK;IAAE;AAC3I;AAEA,MAAME,iBAAiB3B,eAAe;AACtC,gGAAgG;AAChG,8DAA8D;AAC9D,MAAM4B,aAAa,IAAIC;AAEvB,SAASC,WAAWxB,IAAY,EAAEyB,WAAgC;IAChE,IAAIC,YAAYJ,WAAWK,GAAG,CAACF;IAC/B,IAAI,CAACC,WAAW;QACdA,YAAY,IAAIE,KAAKC,SAAS,CAAClB,WAAW;YAAEc;QAAY;QACxDH,WAAWQ,GAAG,CAACL,aAAaC;IAC9B;IACA,OAAOK,MAAMC,IAAI,CAACN,UAAUO,OAAO,CAACjC,OAAO,CAACkC,IAAMA,EAAED,OAAO;AAC7D;AAEA,gGAAgG;AAChG,2FAA2F;AAC3F,SAASE,KAAKC,QAAkB,EAAEC,OAAe;IAC/C,MAAMC,SAAmB,EAAE;IAC3B,IAAIlC,UAAU;IACd,IAAImC,SAAS;IACb,KAAK,MAAMN,WAAWG,SAAU;QAC9B,MAAMI,gBAAgB9C,eAAeuC;QACrC,IAAI7B,QAAQG,MAAM,GAAG,KAAKgC,SAASC,gBAAgBH,SAAS;YAC1DC,OAAO9B,IAAI,CAACJ;YACZA,UAAU;YACVmC,SAAS;QACX;QACAnC,WAAW6B;QACXM,UAAUC;IACZ;IACA,IAAIpC,QAAQG,MAAM,GAAG,GAAG+B,OAAO9B,IAAI,CAACJ;IACpC,OAAOkC;AACT;AAEA,8FAA8F;AAC9F,0GAA0G;AAC1G,SAASG,cAAczC,IAAY,EAAEqC,OAAe;IAClD,MAAMK,YAAYlB,WAAWxB,MAAM;IACnC,MAAM2C,MAAgB,EAAE;IACxB,IAAIvC,UAAU;IACd,IAAImC,SAAS;IACb,MAAMK,QAAQ;QACZ,IAAIxC,QAAQG,MAAM,GAAG,GAAG;YACtBoC,IAAInC,IAAI,CAACJ;YACTA,UAAU;YACVmC,SAAS;QACX;IACF;IACA,KAAK,MAAMM,YAAYH,UAAW;QAChC,MAAMI,iBAAiBpD,eAAemD;QACtC,IAAIC,iBAAiBT,SAAS;YAC5BO;YACAD,IAAInC,IAAI,IAAI2B,KAAKX,WAAWqB,UAAU,SAASR;YAC/C;QACF;QACA,IAAIjC,QAAQG,MAAM,GAAG,KAAKgC,SAASO,iBAAiBT,SAASO;QAC7DxC,WAAWyC;QACXN,UAAUO;IACZ;IACAF;IACA,OAAOD;AACT;AAEA,MAAMI,eAAuC,IAAIC,IAAI;IAAC;IAAQ;IAAS;CAAO;AAE9E,iGAAiG;AACjG,2EAA2E;AAC3E,SAASC,MAAMC,UAAoB,EAAEpC,SAAiB,EAAEC,OAAe,EAAEoC,SAAoB,EAAEC,QAA6B;IAC1H,MAAMpD,OACJ+C,aAAaM,GAAG,CAACF,cAAcC,aAAa,QACxCF,WAAWhC,IAAI,CAAC,QAChB1B,MAAM0D,WAAWhC,IAAI,CAAC,OACnBF,GAAG,CAAC,CAACsC,IAAM/D,YAAY+D,EAAEC,IAAI,GAC7BrC,IAAI,CAAC;IACd,OAAO;QAAEJ;QAAWC;QAASf;IAAK;AACpC;AAEA,kGAAkG;AAClG,yGAAyG;AACzG,SAASwD,cAAcN,UAAoB,EAAEpC,SAAiB,EAAEC,OAAe,EAAEsB,OAAe,EAAEc,SAAoB,EAAEC,QAA6B;IACnJ,MAAMnC,IAAIgC,MAAMC,YAAYpC,WAAWC,SAASoC,WAAWC;IAC3D,IAAIF,WAAW3C,MAAM,KAAK,KAAKb,eAAeuB,EAAEjB,IAAI,IAAIqC,SAAS;QAC/D,OAAOI,cAAcxB,EAAEjB,IAAI,EAAEqC,SAASrB,GAAG,CAAC,CAAChB,OAAU,CAAA;gBAAEc;gBAAWC;gBAASf;gBAAMmB,OAAO;YAAK,CAAA;IAC/F;IACA,OAAO;QAACF;KAAE;AACZ;AAEA,iGAAiG;AACjG,0GAA0G;AAC1G,SAASwC,mBAAmBC,KAAe,EAAE5C,SAAiB,EAAEC,OAAe,EAAEsB,OAAe,EAAEc,SAAoB,EAAEC,QAA6B,EAAEO,OAAO,CAAC;IAC7J,MAAMC,SAAiB,EAAE;IACzB,IAAIV,aAAuB,EAAE;IAC7B,IAAIW,aAAa/C;IACjB,IAAIyB,SAASoB;IACb,IAAK,IAAIG,OAAOhD,WAAWgD,QAAQ/C,SAAS+C,OAAQ;QAClD,MAAMC,WAAWL,KAAK,CAACI,OAAO,EAAE;QAChC,MAAME,MAAMd,WAAW3C,MAAM,GAAG,KAAKgC,SAAS,IAAIlB,iBAAiB;QACnE,MAAM4C,aAAavE,eAAeqE;QAClC,IAAIb,WAAW3C,MAAM,GAAG,KAAKgC,SAASyB,MAAMC,aAAa5B,SAAS;YAChEuB,OAAOpD,IAAI,IAAIgD,cAAcN,YAAYW,YAAYC,OAAO,GAAGzB,SAASc,WAAWC;YACnFF,aAAa,EAAE;YACfX,SAAS;YACTsB,aAAaC;YACbZ,WAAW1C,IAAI,CAACuD;YAChBxB,UAAU0B;YACV;QACF;QACAf,WAAW1C,IAAI,CAACuD;QAChBxB,UAAUyB,MAAMC;IAClB;IACA,IAAIf,WAAW3C,MAAM,GAAG,GAAGqD,OAAOpD,IAAI,IAAIgD,cAAcN,YAAYW,YAAY9C,SAASsB,SAASc,WAAWC;IAC7G,OAAOQ;AACT;AAEA,0FAA0F;AAC1F,8FAA8F;AAC9F,SAASM,WAAWC,WAAoB,EAAET,KAAe,EAAEU,QAAyB;IAClF,MAAM/B,UAAU+B,SAASrE,YAAY;IACrC,MAAMsE,UAAUhC,UAAUzC;IAC1B,MAAM0E,WAA4C,EAAE;IACpD,IAAI5D,QAAgB,EAAE;IACtB,IAAI6D,iBAAiB;IACrB,IAAIhC,SAAS;IAEb,SAASiC;QACP,MAAMC,QAAQhE,SAASC;QACvB,IAAI+D,OAAOH,SAAS9D,IAAI,CAACiE;QACzB/D,QAAQ,EAAE;QACV6D,iBAAiB;QACjBhC,SAAS;IACX;IAEA,mFAAmF;IACnF,2FAA2F;IAC3F,SAASmC,QAAQ1E,IAAY,EAAE2E,QAAgB,EAAE7D,SAAiB,EAAEC,OAAe;QACjFwB,UAAU,AAAC7B,CAAAA,MAAMH,MAAM,GAAG,IAAIc,iBAAiB,CAAA,IAAK3B,eAAeiF;QACnEjE,MAAMF,IAAI,CAAC;YAAEM;YAAWC;YAASf;QAAK;IACxC;IAEA,KAAK,MAAMK,SAAS8D,YAAa;QAC/B,MAAMS,MAAMlB,MAAMmB,KAAK,CAACxE,MAAMS,SAAS,GAAG,GAAGT,MAAMU,OAAO,EAAEG,IAAI,CAAC;QACjE,MAAM4D,cAAcpF,eAAekF;QAEnC,IAAIE,cAAcT,SAAS;YACzB,MAAMV,OAAOjD,MAAMH,MAAM,GAAG,IAAIgC,SAAS;YACzC,KAAK,MAAMtB,KAAKwC,mBAAmBC,OAAOrD,MAAMS,SAAS,EAAET,MAAMU,OAAO,EAAEsB,SAAShC,MAAMC,IAAI,EAAE8D,SAASpE,IAAI,EAAE2D,MAAO;gBACnHjD,MAAMF,IAAI,CAACS;gBACXuD;YACF;YACA;QACF;QAEA,MAAMO,YAAYxF,YAAYc,MAAMkD,IAAI;QACxC,MAAMoB,WAAWP,SAASpE,IAAI,KAAK,QAAQ4E,MAAMG;QAEjD,IAAI1E,MAAMC,IAAI,KAAK,WAAW;YAC5BoE,QAAQK,WAAWJ,UAAUtE,MAAMS,SAAS,EAAET,MAAMU,OAAO;YAC3D;QACF;QAEA,wFAAwF;QACxF,wDAAwD;QACxD,MAAMiE,eAAetE,MAAMH,MAAM,GAAG,KAAKgC,SAASlB,iBAAiByD,cAAcT;QACjF,IAAIW,cAAcR;QAElBE,QAAQK,WAAWJ,UAAUtE,MAAMS,SAAS,EAAET,MAAMU,OAAO;QAC3DwD;QAEA,IAAIA,kBAAkB5E,gBAAgB6E;IACxC;IACAA;IAEA,OAAOF;AACT;AAEA,8FAA8F;AAC9F,8CAA8C;AAC9C,OAAO,SAASG,MAAMvE,MAAe,EAAE+E,IAAY,EAAEnF,IAAmB;IACtE,MAAMsE,WAAWvE,eAAeC;IAChC,MAAM4D,QAAQuB,KAAKC,KAAK,CAAC;IACzB,MAAMC,SAA0C,EAAE;IAClD,KAAK,MAAMC,SAASnF,YAAYC,QAASiF,OAAO3E,IAAI,IAAI0D,WAAWkB,OAAO1B,OAAOU;IACjF,+FAA+F;IAC/F,qHAAqH;IACrH,MAAMiB,SACJjB,SAASpE,IAAI,KAAK,QACdmF,OAAOnE,GAAG,CAAC,CAACsE,IACVA,EAAEnE,KAAK,GACHmE,IACA;YACE,GAAGA,CAAC;YACJtF,MAAM0D,MACHmB,KAAK,CAACS,EAAExE,SAAS,GAAG,GAAGwE,EAAEvE,OAAO,EAChCG,IAAI,CAAC,MACLqE,IAAI;QACT,KAENJ;IACN,mHAAmH;IACnH,OAAOE,OAAOG,MAAM,CAAC,CAACF,IAAMA,EAAEtF,IAAI,CAACuF,IAAI,GAAGhF,MAAM,GAAG,GAAGS,GAAG,CAAC,CAACsE,IAAO,CAAA;YAAExE,WAAWwE,EAAExE,SAAS;YAAEC,SAASuE,EAAEvE,OAAO;YAAEf,MAAMsF,EAAEtF,IAAI;QAAC,CAAA;AAC/H"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/chunk/group.ts"],"sourcesContent":["import { extractText } from './extract.ts';\nimport { parse } from './parse.ts';\nimport { DEFAULT_TARGET_TOKENS, estimateTokens } from './tokens.ts';\nimport type { Block, BlockType, Chunk, ChunkOptions } from './types.ts';\n\nconst PGC_GROUP_SIZE = 2;\nconst OVERSIZE_TRIGGER_MULTIPLE = 2;\n\ninterface ResolvedOptions {\n targetTokens: number;\n text: 'extracted' | 'raw';\n}\n\nfunction resolveOptions(opts?: ChunkOptions): ResolvedOptions {\n return {\n targetTokens: opts?.targetTokens ?? DEFAULT_TARGET_TOKENS,\n text: opts?.text ?? 'raw',\n };\n}\n\n// A heading of any depth ends the current scope and starts a new one (D1); the heading block\n// itself is carried into the new scope, where it joins that scope's first group (F7/F10).\nfunction splitScopes(blocks: Block[]): Block[][] {\n const scopes: Block[][] = [];\n let current: Block[] = [];\n for (const block of blocks) {\n if (block.type === 'heading' && current.length > 0) {\n scopes.push(current);\n current = [];\n }\n current.push(block);\n }\n if (current.length > 0) scopes.push(current);\n return scopes;\n}\n\ninterface Part {\n startLine: number;\n endLine: number;\n text: string;\n // True for a sub-line split piece: its text is already the final slice, not the whole line --\n // group()'s extent-based raw re-slice must not touch it (siblings share startLine === endLine).\n final?: boolean;\n}\n\nfunction finalize(parts: Part[]): (Chunk & { final?: boolean }) | undefined {\n if (parts.length === 0) return undefined;\n const first = parts[0];\n const last = parts[parts.length - 1];\n return { startLine: first.startLine, endLine: last.endLine, text: parts.map((p) => p.text).join('\\n'), final: parts.some((p) => p.final) };\n}\n\nconst NEWLINE_TOKENS = estimateTokens('\\n');\n// Built on first use and kept: each construction is ~3.5 ms, and only an oversize block is ever\n// split, so no command pays for a segmenter it never reaches.\nconst SEGMENTERS = new Map<string, Intl.Segmenter>();\n\nfunction segmentsOf(text: string, granularity: 'sentence' | 'word'): string[] {\n let segmenter = SEGMENTERS.get(granularity);\n if (!segmenter) {\n segmenter = new Intl.Segmenter(undefined, { granularity });\n SEGMENTERS.set(granularity, segmenter);\n }\n return Array.from(segmenter.segment(text), (s) => s.segment);\n}\n\n// Greedily packs segments (already contiguous, tiling the source text with no gaps) into groups\n// of at most `working` estimated tokens; a lone segment over `working` still stands alone.\nfunction pack(segments: string[], working: number): string[] {\n const groups: string[] = [];\n let current = '';\n let tokens = 0;\n for (const segment of segments) {\n const segmentTokens = estimateTokens(segment);\n if (current.length > 0 && tokens + segmentTokens > working) {\n groups.push(current);\n current = '';\n tokens = 0;\n }\n current += segment;\n tokens += segmentTokens;\n }\n if (current.length > 0) groups.push(current);\n return groups;\n}\n\n// Line-split alone can't shrink a lone dense line (the CJK case): falls back to sentence then\n// word boundaries (Intl.Segmenter, the same grapheme-safe engine as segment.ts), mode-agnostic on `text`.\nfunction splitLineText(text: string, working: number): string[] {\n const sentences = segmentsOf(text, 'sentence');\n const out: string[] = [];\n let current = '';\n let tokens = 0;\n const flush = () => {\n if (current.length > 0) {\n out.push(current);\n current = '';\n tokens = 0;\n }\n };\n for (const sentence of sentences) {\n const sentenceTokens = estimateTokens(sentence);\n if (sentenceTokens > working) {\n flush();\n out.push(...pack(segmentsOf(sentence, 'word'), working));\n continue;\n }\n if (current.length > 0 && tokens + sentenceTokens > working) flush();\n current += sentence;\n tokens += sentenceTokens;\n }\n flush();\n return out;\n}\n\nconst ATOMIC_TYPES: ReadonlySet<BlockType> = new Set(['code', 'table', 'list']);\n\n// Atomic (code/table/list) pieces are always a raw line slice: re-parsing a table's later pieces\n// without their header/delimiter rows would demote them to paragraph text.\nfunction piece(pieceLines: string[], startLine: number, endLine: number, blockType: BlockType, textMode: 'extracted' | 'raw'): Part {\n const text =\n ATOMIC_TYPES.has(blockType) || textMode === 'raw'\n ? pieceLines.join('\\n')\n : parse(pieceLines.join('\\n'))\n .map((b) => extractText(b))\n .join('\\n');\n return { startLine, endLine, text };\n}\n\n// A one-line piece over working can't shrink via another line-boundary pass (rule 5's gap), so it\n// splits at sentence/word boundaries instead; `final` stops group() re-deriving its text by extent (F5).\nfunction finalizePiece(pieceLines: string[], startLine: number, endLine: number, working: number, blockType: BlockType, textMode: 'extracted' | 'raw'): Part[] {\n const p = piece(pieceLines, startLine, endLine, blockType, textMode);\n if (pieceLines.length === 1 && estimateTokens(p.text) > working) {\n return splitLineText(p.text, working).map((text) => ({ startLine, endLine, text, final: true }));\n }\n return [p];\n}\n\n// A block over 2x working size splits at line boundaries into pieces each <= working size, never\n// mid-line. `seed`: pending tokens (e.g. a heading) the first piece must join, checked against the limit.\nfunction splitOversizeBlock(lines: string[], startLine: number, endLine: number, working: number, blockType: BlockType, textMode: 'extracted' | 'raw', seed = 0): Part[] {\n const pieces: Part[] = [];\n let pieceLines: string[] = [];\n let pieceStart = startLine;\n let tokens = seed;\n for (let line = startLine; line <= endLine; line++) {\n const lineText = lines[line - 1];\n const sep = pieceLines.length > 0 || tokens > 0 ? NEWLINE_TOKENS : 0;\n const lineTokens = estimateTokens(lineText);\n if (pieceLines.length > 0 && tokens + sep + lineTokens > working) {\n pieces.push(...finalizePiece(pieceLines, pieceStart, line - 1, working, blockType, textMode));\n pieceLines = [];\n tokens = 0;\n pieceStart = line;\n pieceLines.push(lineText);\n tokens += lineTokens;\n continue;\n }\n pieceLines.push(lineText);\n tokens += sep + lineTokens;\n }\n if (pieceLines.length > 0) pieces.push(...finalizePiece(pieceLines, pieceStart, endLine, working, blockType, textMode));\n return pieces;\n}\n\n// One heading scope's groups (D1): a heading opens the first group, and an oversize block\n// (rule 5, including an oversize heading) splits into pieces that each close their own group.\nfunction groupScope(scopeBlocks: Block[], lines: string[], resolved: ResolvedOptions): (Chunk & { final?: boolean })[] {\n const working = resolved.targetTokens;\n const trigger = working * OVERSIZE_TRIGGER_MULTIPLE;\n const finished: (Chunk & { final?: boolean })[] = [];\n let parts: Part[] = [];\n let paragraphCount = 0;\n let tokens = 0;\n\n function close(): void {\n const group = finalize(parts);\n if (group) finished.push(group);\n parts = [];\n paragraphCount = 0;\n tokens = 0;\n }\n\n // tokens tracks the active text mode's own estimate (a newline between parts costs\n // NEWLINE_TOKENS too), so packing decisions size the text the chunk will actually ship as.\n function addPart(text: string, sizeText: string, startLine: number, endLine: number): void {\n tokens += (parts.length > 0 ? NEWLINE_TOKENS : 0) + estimateTokens(sizeText);\n parts.push({ startLine, endLine, text });\n }\n\n for (const block of scopeBlocks) {\n const raw = lines.slice(block.startLine - 1, block.endLine).join('\\n');\n const blockTokens = estimateTokens(raw);\n\n if (blockTokens > trigger) {\n const seed = parts.length > 0 ? tokens : 0;\n for (const p of splitOversizeBlock(lines, block.startLine, block.endLine, working, block.type, resolved.text, seed)) {\n parts.push(p);\n close();\n }\n continue;\n }\n\n const extracted = extractText(block);\n const sizeText = resolved.text === 'raw' ? raw : extracted;\n\n if (block.type === 'heading') {\n addPart(extracted, sizeText, block.startLine, block.endLine);\n continue;\n }\n\n // The 2x-working invariant holds even under pgc's paper-faithful 2-paragraph pairing --\n // close first if the pair about to form would cross it.\n const pairOversize = parts.length > 0 && tokens + NEWLINE_TOKENS + blockTokens > trigger;\n if (pairOversize) close();\n\n addPart(extracted, sizeText, block.startLine, block.endLine);\n paragraphCount++;\n\n if (paragraphCount >= PGC_GROUP_SIZE) close();\n }\n close();\n\n return finished;\n}\n\n// Groups already-parsed blocks per opts (D1/D3), against the same body the blocks were parsed\n// from (line lookups for oversize splitting).\nexport function group(blocks: Block[], body: string, opts?: ChunkOptions): Chunk[] {\n const resolved = resolveOptions(opts);\n const lines = body.split('\\n');\n const chunks: (Chunk & { final?: boolean })[] = [];\n for (const scope of splitScopes(blocks)) chunks.push(...groupScope(scope, lines, resolved));\n // 'raw': the chunk's own source lines verbatim, replacing the flavor-resolved join above (D9).\n // A `final` chunk already carries its own slice's raw text; re-slicing by extent would return the whole shared line.\n const texted =\n resolved.text === 'raw'\n ? chunks.map((c) =>\n c.final\n ? c\n : {\n ...c,\n text: lines\n .slice(c.startLine - 1, c.endLine)\n .join('\\n')\n .trim(),\n }\n )\n : chunks;\n // A group can be all-blank (flavor-stripped to nothing, or a raw slice of pure syntax); it never produces a chunk.\n return texted.filter((c) => c.text.trim().length > 0).map((c) => ({ startLine: c.startLine, endLine: c.endLine, text: c.text }));\n}\n"],"names":["extractText","parse","DEFAULT_TARGET_TOKENS","estimateTokens","PGC_GROUP_SIZE","OVERSIZE_TRIGGER_MULTIPLE","resolveOptions","opts","targetTokens","text","splitScopes","blocks","scopes","current","block","type","length","push","finalize","parts","undefined","first","last","startLine","endLine","map","p","join","final","some","NEWLINE_TOKENS","SEGMENTERS","Map","segmentsOf","granularity","segmenter","get","Intl","Segmenter","set","Array","from","segment","s","pack","segments","working","groups","tokens","segmentTokens","splitLineText","sentences","out","flush","sentence","sentenceTokens","ATOMIC_TYPES","Set","piece","pieceLines","blockType","textMode","has","b","finalizePiece","splitOversizeBlock","lines","seed","pieces","pieceStart","line","lineText","sep","lineTokens","groupScope","scopeBlocks","resolved","trigger","finished","paragraphCount","close","group","addPart","sizeText","raw","slice","blockTokens","extracted","pairOversize","body","split","chunks","scope","texted","c","trim","filter"],"mappings":"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"}
|