@chatpanel/events 0.102.0 → 0.104.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.
Files changed (139) hide show
  1. package/adapters.js +1 -83
  2. package/adaptive-tool-policy.js +1 -45
  3. package/agent.js +2 -255
  4. package/attribution.js +1 -132
  5. package/backup-envelope.js +1 -221
  6. package/board-tool.js +2 -165
  7. package/budget.js +1 -116
  8. package/capability.js +1 -126
  9. package/citations.js +5 -127
  10. package/client-prefs.js +1 -148
  11. package/context-attachments.js +8 -144
  12. package/cowriter-router.js +1 -83
  13. package/cowriter-writer.js +19 -193
  14. package/cowriter.js +1 -190
  15. package/curate.js +3 -509
  16. package/distance.js +1 -124
  17. package/engine.js +1 -131
  18. package/entitlement.js +1 -332
  19. package/entity.js +1 -354
  20. package/event.js +1 -175
  21. package/extraction.js +4 -293
  22. package/failover.js +1 -93
  23. package/find-tool.js +1 -66
  24. package/flowchart.js +2 -644
  25. package/gate.js +1 -74
  26. package/harness.js +4 -101
  27. package/index.js +1 -345
  28. package/invariants.js +1 -174
  29. package/job.js +3 -147
  30. package/kernel.js +1 -255
  31. package/knowledge-derive.js +1 -267
  32. package/knowledge.js +3 -230
  33. package/library.js +11 -280
  34. package/loop.js +1 -132
  35. package/manifest.js +1 -107
  36. package/markdown-authoring.js +11 -288
  37. package/markdown-render.js +0 -0
  38. package/mcp-client.js +8 -358
  39. package/mcp-dispatch.js +1 -52
  40. package/mcp-errors.js +1 -106
  41. package/mcp-manager.js +1 -127
  42. package/media-transcript.js +8 -531
  43. package/meeting-analyzers.js +1 -83
  44. package/meeting-insights.js +7 -164
  45. package/meeting-shape.js +1 -282
  46. package/meeting-text.js +5 -94
  47. package/memory.js +2 -638
  48. package/model-candidates.js +1 -358
  49. package/model-health.js +1 -182
  50. package/model-ledger.js +1 -228
  51. package/model-picker.js +1 -188
  52. package/note-actions.js +15 -246
  53. package/note-graph.js +1 -158
  54. package/note-links.js +1 -68
  55. package/note-mentions.js +8 -114
  56. package/note-plan.js +9 -107
  57. package/note-research.js +1 -135
  58. package/observability.js +1 -119
  59. package/omni.js +1 -125
  60. package/order.js +1 -78
  61. package/package.json +4 -5
  62. package/pdf-layout.js +7 -253
  63. package/policy.js +1 -0
  64. package/project-run.js +20 -444
  65. package/project.js +1 -170
  66. package/promotion.js +1 -171
  67. package/queue.js +1 -98
  68. package/reach.js +1 -23
  69. package/recipe-tool.js +2 -161
  70. package/recipe.js +1 -264
  71. package/record-list.js +2 -110
  72. package/recruit.js +3 -456
  73. package/redaction-tokens.js +1 -61
  74. package/ref.js +1 -55
  75. package/registry.js +1 -240
  76. package/route-graph.js +1 -115
  77. package/route-strategies.js +2 -232
  78. package/router.js +2 -880
  79. package/rrf.js +1 -89
  80. package/rules.js +1 -142
  81. package/schedule.js +1 -663
  82. package/scm-connection.js +1 -180
  83. package/scopes.js +1 -12
  84. package/scorecard.js +1 -335
  85. package/search-engines.js +1 -81
  86. package/skill-manifest.js +1 -330
  87. package/skill-scan.js +3 -209
  88. package/skill-sources.js +1 -162
  89. package/skill-vars.js +2 -252
  90. package/slash-commands.js +1 -158
  91. package/source-gate.js +1 -92
  92. package/sources-retrieval.js +5 -186
  93. package/sources.js +1 -273
  94. package/store.js +1 -171
  95. package/structured.js +4 -901
  96. package/subject-kinds.js +1 -5
  97. package/subject-name.js +1 -96
  98. package/sync-plan.js +1 -213
  99. package/synthesis.js +3 -123
  100. package/tags.js +1 -203
  101. package/team-board.js +6 -337
  102. package/team-cache.js +2 -63
  103. package/team-observe.js +1 -118
  104. package/team-org.js +1 -442
  105. package/team-plan.js +4 -131
  106. package/team-record.js +1 -225
  107. package/team-run.js +9 -652
  108. package/team-subtask.js +2 -191
  109. package/team-task.js +2 -124
  110. package/team-tool.js +2 -196
  111. package/team-trail.js +2 -73
  112. package/team-worklog.js +2 -149
  113. package/team.js +1 -302
  114. package/text-search.js +1 -155
  115. package/theme.js +17 -154
  116. package/titles.js +4 -285
  117. package/tool-discovery.js +1 -87
  118. package/tool-dispatch.js +5 -230
  119. package/tool-groups.js +1 -81
  120. package/tool-hints.js +10 -185
  121. package/tool-loop-guard.js +4 -182
  122. package/tool-need.js +2 -124
  123. package/tool-result.js +2 -438
  124. package/tool-round.js +0 -0
  125. package/tool-schema.js +1 -155
  126. package/tool-traits.js +1 -182
  127. package/toolset.js +2 -88
  128. package/trajectory.js +1 -530
  129. package/turn-loop.js +4 -440
  130. package/upcast.js +1 -37
  131. package/vault.js +2 -250
  132. package/view.js +1 -88
  133. package/voice-intents.js +2 -1488
  134. package/voice-speaker.js +1 -98
  135. package/weather-tool.js +1 -65
  136. package/weather.js +2 -211
  137. package/web-search-tool.js +14 -102
  138. package/web-search.js +1 -160
  139. package/widget.js +1 -153
@@ -1,288 +1,11 @@
1
- // The editing gestures that make writing markdown feel like writing, not like typing syntax.
2
- //
3
- // Outline, list continuation, format toggles and document stats are all the same shape of
4
- // problem: given the document text and where the caret is, what should the text and the
5
- // caret become. None of them needs a DOM — which is precisely why they do not belong in a
6
- // `<textarea>` keydown handler, where Notes would have had to write them twice (once for
7
- // the classic surface, once for CodeMirror) and a mobile client a third time.
8
- //
9
- // Every function here is pure and returns an EDIT ({ text, selStart, selEnd }) rather than
10
- // mutating a surface, so the caller only has to apply a range and set a selection.
11
-
12
- /** ``` or ~~~ opening/closing a fenced block. Headings inside a fence are code, not structure. */
13
- const FENCE_RE = /^\s{0,3}(`{3,}|~{3,})/;
14
-
15
- /**
16
- * The heading structure of a markdown document.
17
- *
18
- * Fenced code is skipped: a `# comment` inside a shell example is not a section, and an
19
- * outline that jumps into code is worse than no outline. Setext headings (`===` / `---`
20
- * underlines) are recognised too, because notes pasted from other tools use them.
21
- */
22
- export function outlineOf(markdown) {
23
- const doc = String(markdown ?? '');
24
- const lines = doc.split('\n');
25
- const out = [];
26
- let offset = 0;
27
- let fence = '';
28
- for (let i = 0; i < lines.length; i += 1) {
29
- const line = lines[i];
30
- const fenceHit = line.match(FENCE_RE);
31
- if (fenceHit) {
32
- if (!fence) fence = fenceHit[1][0];
33
- else if (fenceHit[1][0] === fence) fence = '';
34
- offset += line.length + 1;
35
- continue;
36
- }
37
- if (!fence) {
38
- const atx = line.match(/^(#{1,6})\s+(.*?)\s*#*\s*$/);
39
- const next = lines[i + 1];
40
- const setext = !atx && line.trim() && next && next.match(/^\s{0,3}(=+|-+)\s*$/);
41
- if (atx) {
42
- out.push({ level: atx[1].length, text: atx[2].trim(), line: i, start: offset, end: offset + line.length });
43
- } else if (setext) {
44
- out.push({ level: setext[1][0] === '=' ? 1 : 2, text: line.trim(), line: i, start: offset, end: offset + line.length });
45
- }
46
- }
47
- offset += line.length + 1;
48
- }
49
- return out;
50
- }
51
-
52
- /** ` - [ ] item` → its indent, marker, checkbox and content. Null when the line is not a list item. */
53
- export function parseListItem(line) {
54
- const m = String(line ?? '').match(/^(\s*)([-*+]|\d{1,9}[.)])\s+(\[[ xX]\]\s+)?(.*)$/);
55
- if (!m) return null;
56
- return {
57
- indent: m[1],
58
- marker: m[2],
59
- ordered: /\d/.test(m[2]),
60
- checkbox: m[3] ? m[3].trimEnd() : '',
61
- content: m[4],
62
- };
63
- }
64
-
65
- function lineBoundsAt(doc, pos) {
66
- const start = doc.lastIndexOf('\n', Math.max(0, pos - 1)) + 1;
67
- const nl = doc.indexOf('\n', pos);
68
- return { start, end: nl === -1 ? doc.length : nl };
69
- }
70
-
71
- /**
72
- * What Enter should do inside a list.
73
- *
74
- * Continues the list with the same indent and marker (incrementing an ordered one), and
75
- * ENDS it when the item is empty — pressing Enter twice is how everyone expects to leave a
76
- * list, and without that rule the user is left deleting a bullet they did not ask for.
77
- *
78
- * Returns null when the caret is not in a list item, so the caller can let the key fall
79
- * through to the surface's own newline handling.
80
- */
81
- export function continueList(text, cursor) {
82
- const doc = String(text ?? '');
83
- const pos = Math.max(0, Math.min(cursor, doc.length));
84
- const { start, end } = lineBoundsAt(doc, pos);
85
- const item = parseListItem(doc.slice(start, end));
86
- if (!item) return null;
87
-
88
- // Empty item → the user is done with the list. Clear the marker instead of adding another.
89
- if (!item.content.trim()) {
90
- return { text: `${doc.slice(0, start)}\n${doc.slice(end)}`, selStart: start + 1, selEnd: start + 1 };
91
- }
92
-
93
- const marker = item.ordered
94
- ? `${(parseInt(item.marker, 10) || 0) + 1}${item.marker.slice(-1)}`
95
- : item.marker;
96
- // A checked box never continues as checked — the next item is new work, not done work.
97
- const box = item.checkbox ? '[ ] ' : '';
98
- const insert = `\n${item.indent}${marker} ${box}`;
99
- const caret = pos + insert.length;
100
- return { text: doc.slice(0, pos) + insert + doc.slice(pos), selStart: caret, selEnd: caret };
101
- }
102
-
103
- /**
104
- * Indent or outdent every list item the selection touches.
105
- *
106
- * Whole lines, not the caret's line alone, so Tab on a multi-line selection does the
107
- * obvious thing. Outdent removes at most one level and never eats non-space characters.
108
- */
109
- export function indentSelection(text, selStart, selEnd, dir = 1, unit = ' ') {
110
- const doc = String(text ?? '');
111
- const from = lineBoundsAt(doc, Math.min(selStart, selEnd)).start;
112
- const to = lineBoundsAt(doc, Math.max(selStart, selEnd)).end;
113
- const lines = doc.slice(from, to).split('\n');
114
- let firstDelta = 0;
115
- let total = 0;
116
- const shifted = lines.map((line, i) => {
117
- let next = line;
118
- if (dir >= 0) {
119
- if (line.trim()) next = unit + line;
120
- } else {
121
- const m = line.match(new RegExp(`^(${unit}|\\t|\\s{1,${unit.length}})`));
122
- if (m) next = line.slice(m[0].length);
123
- }
124
- const delta = next.length - line.length;
125
- if (i === 0) firstDelta = delta;
126
- total += delta;
127
- return next;
128
- });
129
- return {
130
- text: doc.slice(0, from) + shifted.join('\n') + doc.slice(to),
131
- selStart: Math.max(from, Math.min(selStart, selEnd) + firstDelta),
132
- selEnd: Math.max(from, Math.max(selStart, selEnd) + total),
133
- };
134
- }
135
-
136
- /** Inline formats, as the pair of markers that wrap the selection. */
137
- const WRAPS = {
138
- bold: '**',
139
- italic: '*',
140
- code: '`',
141
- strike: '~~',
142
- highlight: '==',
143
- };
144
-
145
- /**
146
- * Toggle an inline format around the selection.
147
- *
148
- * Toggle, not apply: selecting already-bold text and pressing ⌘B must UNbold it, both when
149
- * the markers are inside the selection and when they sit just outside it (which is what a
150
- * double-click on the word gives you). Getting only the first case right is the usual bug,
151
- * and it leaves users with `****text****`.
152
- */
153
- export function toggleWrap(text, selStart, selEnd, kind) {
154
- const doc = String(text ?? '');
155
- const mark = WRAPS[kind];
156
- if (!mark) return null;
157
- let a = Math.min(selStart, selEnd);
158
- let b = Math.max(selStart, selEnd);
159
-
160
- // Nothing selected: grow to the word under the caret so ⌘B on a word just works.
161
- if (a === b) {
162
- const { start, end } = lineBoundsAt(doc, a);
163
- const line = doc.slice(start, end);
164
- let ws = a - start;
165
- let we = a - start;
166
- while (ws > 0 && /\w/.test(line[ws - 1])) ws -= 1;
167
- while (we < line.length && /\w/.test(line[we])) we += 1;
168
- if (we > ws) { a = start + ws; b = start + we; }
169
- }
170
-
171
- const inner = doc.slice(a, b);
172
- // Markers inside the selection.
173
- if (inner.length >= mark.length * 2 && inner.startsWith(mark) && inner.endsWith(mark)) {
174
- const stripped = inner.slice(mark.length, -mark.length);
175
- return { text: doc.slice(0, a) + stripped + doc.slice(b), selStart: a, selEnd: a + stripped.length };
176
- }
177
- // Markers hugging the selection.
178
- if (doc.slice(a - mark.length, a) === mark && doc.slice(b, b + mark.length) === mark) {
179
- return {
180
- text: doc.slice(0, a - mark.length) + inner + doc.slice(b + mark.length),
181
- selStart: a - mark.length,
182
- selEnd: b - mark.length,
183
- };
184
- }
185
- return {
186
- text: doc.slice(0, a) + mark + inner + mark + doc.slice(b),
187
- selStart: a + mark.length,
188
- selEnd: b + mark.length,
189
- };
190
- }
191
-
192
- /**
193
- * Toggle a line-level prefix (quote, bullet, number, checkbox) over the selected lines.
194
- *
195
- * Uniformly: if EVERY touched line already has the prefix the gesture removes it, otherwise
196
- * it adds it to all of them. A per-line toggle on a mixed selection scrambles the block.
197
- */
198
- export function toggleLinePrefix(text, selStart, selEnd, kind) {
199
- const doc = String(text ?? '');
200
- const from = lineBoundsAt(doc, Math.min(selStart, selEnd)).start;
201
- const to = lineBoundsAt(doc, Math.max(selStart, selEnd)).end;
202
- const lines = doc.slice(from, to).split('\n');
203
- const RE = {
204
- quote: /^(\s*)>\s?/,
205
- bullet: /^(\s*)[-*+]\s+/,
206
- number: /^(\s*)\d{1,9}[.)]\s+/,
207
- task: /^(\s*)[-*+]\s+\[[ xX]\]\s+/,
208
- }[kind];
209
- if (!RE) return null;
210
- const on = lines.every((l) => !l.trim() || RE.test(l));
211
- let n = 0;
212
- const next = lines.map((line) => {
213
- if (!line.trim()) return line;
214
- if (on) return line.replace(RE, '$1');
215
- const indent = line.match(/^\s*/)[0];
216
- const body = line.slice(indent.length).replace(/^(>\s?|[-*+]\s+(\[[ xX]\]\s+)?|\d{1,9}[.)]\s+)/, '');
217
- n += 1;
218
- const prefix = { quote: '> ', bullet: '- ', number: `${n}. `, task: '- [ ] ' }[kind];
219
- return indent + prefix + body;
220
- });
221
- const body = next.join('\n');
222
- return { text: doc.slice(0, from) + body + doc.slice(to), selStart: from, selEnd: from + body.length };
223
- }
224
-
225
- /** Toggle `- [ ]` ⇄ `- [x]` on the line at `cursor`. Null when that line has no checkbox. */
226
- export function toggleTask(text, cursor) {
227
- const doc = String(text ?? '');
228
- const { start, end } = lineBoundsAt(doc, Math.max(0, Math.min(cursor, doc.length)));
229
- const line = doc.slice(start, end);
230
- const m = line.match(/^(\s*[-*+]\s+\[)([ xX])(\]\s*)/);
231
- if (!m) return null;
232
- const flipped = `${m[1]}${m[2] === ' ' ? 'x' : ' '}${m[3]}${line.slice(m[0].length)}`;
233
- return { text: doc.slice(0, start) + flipped + doc.slice(end), selStart: cursor, selEnd: cursor };
234
- }
235
-
236
- /**
237
- * Wrap the selection as a markdown link.
238
- *
239
- * When the selection already looks like a URL it becomes the TARGET with an empty label,
240
- * otherwise it becomes the label — which is what the user meant in each case, and saves
241
- * retyping the half they already have. The caret lands on the empty half.
242
- */
243
- export function toggleLink(text, selStart, selEnd, url = '') {
244
- const doc = String(text ?? '');
245
- const a = Math.min(selStart, selEnd);
246
- const b = Math.max(selStart, selEnd);
247
- const inner = doc.slice(a, b);
248
- const looksUrl = /^(https?:\/\/|mailto:)\S+$/i.test(inner.trim());
249
- const label = looksUrl ? '' : inner;
250
- const target = url || (looksUrl ? inner.trim() : '');
251
- const out = `[${label}](${target})`;
252
- const caret = looksUrl ? a + 1 : a + out.length - 1;
253
- return { text: doc.slice(0, a) + out + doc.slice(b), selStart: caret, selEnd: caret + (looksUrl ? 0 : 0) };
254
- }
255
-
256
- /** Words in a string, counting a run of any non-whitespace as one word. */
257
- function countWords(s) {
258
- const t = String(s ?? '').trim();
259
- return t ? t.split(/\s+/).length : 0;
260
- }
261
-
262
- /**
263
- * Word / character counts and a reading estimate.
264
- *
265
- * 200 wpm is the usual prose figure. Reading time is reported in whole minutes with a floor
266
- * of one, because "0 min read" is noise and a sub-minute note does not need a number at all.
267
- */
268
- export function docStats(text, { wpm = 200 } = {}) {
269
- const doc = String(text ?? '');
270
- const words = countWords(doc);
271
- return {
272
- words,
273
- chars: doc.length,
274
- charsNoSpaces: doc.replace(/\s/g, '').length,
275
- lines: doc ? doc.split('\n').length : 0,
276
- readingMinutes: words ? Math.max(1, Math.round(words / wpm)) : 0,
277
- };
278
- }
279
-
280
- /** Stats for the selection when there is one, otherwise for the whole document. */
281
- export function selectionStats(text, selStart, selEnd, opts) {
282
- const doc = String(text ?? '');
283
- const a = Math.min(selStart, selEnd);
284
- const b = Math.max(selStart, selEnd);
285
- return a === b
286
- ? { ...docStats(doc, opts), selection: false }
287
- : { ...docStats(doc.slice(a, b), opts), selection: true };
288
- }
1
+ const $=/^\s{0,3}(`{3,}|~{3,})/;function w(o){const s=String(o??"").split(`
2
+ `),l=[];let n=0,e="";for(let t=0;t<s.length;t+=1){const i=s[t],r=i.match($);if(r){e?r[1][0]===e&&(e=""):e=r[1][0],n+=i.length+1;continue}if(!e){const a=i.match(/^(#{1,6})\s+(.*?)\s*#*\s*$/),m=s[t+1],u=!a&&i.trim()&&m&&m.match(/^\s{0,3}(=+|-+)\s*$/);a?l.push({level:a[1].length,text:a[2].trim(),line:t,start:n,end:n+i.length}):u&&l.push({level:u[1][0]==="="?1:2,text:i.trim(),line:t,start:n,end:n+i.length})}n+=i.length+1}return l}function S(o){const c=String(o??"").match(/^(\s*)([-*+]|\d{1,9}[.)])\s+(\[[ xX]\]\s+)?(.*)$/);return c?{indent:c[1],marker:c[2],ordered:/\d/.test(c[2]),checkbox:c[3]?c[3].trimEnd():"",content:c[4]}:null}function f(o,c){const s=o.lastIndexOf(`
3
+ `,Math.max(0,c-1))+1,l=o.indexOf(`
4
+ `,c);return{start:s,end:l===-1?o.length:l}}function E(o,c){const s=String(o??""),l=Math.max(0,Math.min(c,s.length)),{start:n,end:e}=f(s,l),t=S(s.slice(n,e));if(!t)return null;if(!t.content.trim())return{text:`${s.slice(0,n)}
5
+ ${s.slice(e)}`,selStart:n+1,selEnd:n+1};const i=t.ordered?`${(parseInt(t.marker,10)||0)+1}${t.marker.slice(-1)}`:t.marker,r=t.checkbox?"[ ] ":"",a=`
6
+ ${t.indent}${i} ${r}`,m=l+a.length;return{text:s.slice(0,l)+a+s.slice(l),selStart:m,selEnd:m}}function W(o,c,s,l=1,n=" "){const e=String(o??""),t=f(e,Math.min(c,s)).start,i=f(e,Math.max(c,s)).end,r=e.slice(t,i).split(`
7
+ `);let a=0,m=0;const u=r.map((h,g)=>{let x=h;if(l>=0)h.trim()&&(x=n+h);else{const p=h.match(new RegExp(`^(${n}|\\t|\\s{1,${n.length}})`));p&&(x=h.slice(p[0].length))}const d=x.length-h.length;return g===0&&(a=d),m+=d,x});return{text:e.slice(0,t)+u.join(`
8
+ `)+e.slice(i),selStart:Math.max(t,Math.min(c,s)+a),selEnd:Math.max(t,Math.max(c,s)+m)}}const b={bold:"**",italic:"*",code:"`",strike:"~~",highlight:"=="};function L(o,c,s,l){const n=String(o??""),e=b[l];if(!e)return null;let t=Math.min(c,s),i=Math.max(c,s);if(t===i){const{start:a,end:m}=f(n,t),u=n.slice(a,m);let h=t-a,g=t-a;for(;h>0&&/\w/.test(u[h-1]);)h-=1;for(;g<u.length&&/\w/.test(u[g]);)g+=1;g>h&&(t=a+h,i=a+g)}const r=n.slice(t,i);if(r.length>=e.length*2&&r.startsWith(e)&&r.endsWith(e)){const a=r.slice(e.length,-e.length);return{text:n.slice(0,t)+a+n.slice(i),selStart:t,selEnd:t+a.length}}return n.slice(t-e.length,t)===e&&n.slice(i,i+e.length)===e?{text:n.slice(0,t-e.length)+r+n.slice(i+e.length),selStart:t-e.length,selEnd:i-e.length}:{text:n.slice(0,t)+e+r+e+n.slice(i),selStart:t+e.length,selEnd:i+e.length}}function R(o,c,s,l){const n=String(o??""),e=f(n,Math.min(c,s)).start,t=f(n,Math.max(c,s)).end,i=n.slice(e,t).split(`
9
+ `),r={quote:/^(\s*)>\s?/,bullet:/^(\s*)[-*+]\s+/,number:/^(\s*)\d{1,9}[.)]\s+/,task:/^(\s*)[-*+]\s+\[[ xX]\]\s+/}[l];if(!r)return null;const a=i.every(g=>!g.trim()||r.test(g));let m=0;const h=i.map(g=>{if(!g.trim())return g;if(a)return g.replace(r,"$1");const x=g.match(/^\s*/)[0],d=g.slice(x.length).replace(/^(>\s?|[-*+]\s+(\[[ xX]\]\s+)?|\d{1,9}[.)]\s+)/,"");m+=1;const p={quote:"> ",bullet:"- ",number:`${m}. `,task:"- [ ] "}[l];return x+p+d}).join(`
10
+ `);return{text:n.slice(0,e)+h+n.slice(t),selStart:e,selEnd:e+h.length}}function X(o,c){const s=String(o??""),{start:l,end:n}=f(s,Math.max(0,Math.min(c,s.length))),e=s.slice(l,n),t=e.match(/^(\s*[-*+]\s+\[)([ xX])(\]\s*)/);if(!t)return null;const i=`${t[1]}${t[2]===" "?"x":" "}${t[3]}${e.slice(t[0].length)}`;return{text:s.slice(0,l)+i+s.slice(n),selStart:c,selEnd:c}}function v(o,c,s,l=""){const n=String(o??""),e=Math.min(c,s),t=Math.max(c,s),i=n.slice(e,t),r=/^(https?:\/\/|mailto:)\S+$/i.test(i.trim()),a=r?"":i,m=l||(r?i.trim():""),u=`[${a}](${m})`,h=r?e+1:e+u.length-1;return{text:n.slice(0,e)+u+n.slice(t),selStart:h,selEnd:h+0}}function k(o){const c=String(o??"").trim();return c?c.split(/\s+/).length:0}function M(o,{wpm:c=200}={}){const s=String(o??""),l=k(s);return{words:l,chars:s.length,charsNoSpaces:s.replace(/\s/g,"").length,lines:s?s.split(`
11
+ `).length:0,readingMinutes:l?Math.max(1,Math.round(l/c)):0}}function y(o,c,s,l){const n=String(o??""),e=Math.min(c,s),t=Math.max(c,s);return e===t?{...M(n,l),selection:!1}:{...M(n.slice(e,t),l),selection:!0}}export{E as continueList,M as docStats,W as indentSelection,w as outlineOf,S as parseListItem,y as selectionStats,R as toggleLinePrefix,v as toggleLink,X as toggleTask,L as toggleWrap};
Binary file