@helping-ai-workflow/md2doc 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/editor/cell-range.js +85 -0
- package/lib/editor/cli.js +2 -2
- package/lib/editor/client.js +847 -14
- package/lib/editor/server.js +209 -47
- package/lib/md2doc.js +13 -0
- package/package.json +2 -2
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/* Table cell-range selection — the pure model.
|
|
3
|
+
Spec: docs/superpowers/specs/2026-09-24-editor-cell-range-design.md.
|
|
4
|
+
UMD, same shape as selection.js: require-able in node for the unit tests,
|
|
5
|
+
injected into the editor page as window.md2docCellRange. Points are
|
|
6
|
+
{r, c}: r indexes the table's rows with the header row as 0, c the cell
|
|
7
|
+
index within that row. */
|
|
8
|
+
(function (root, factory) {
|
|
9
|
+
if (typeof module === 'object' && module.exports) module.exports = factory();
|
|
10
|
+
else root.md2docCellRange = factory();
|
|
11
|
+
})(typeof self !== 'undefined' ? self : this, function () {
|
|
12
|
+
function clamp(n, lo, hi) { return Math.max(lo, Math.min(hi, n)); }
|
|
13
|
+
|
|
14
|
+
function normalize(a, f) {
|
|
15
|
+
return { top: Math.min(a.r, f.r), left: Math.min(a.c, f.c),
|
|
16
|
+
bottom: Math.max(a.r, f.r), right: Math.max(a.c, f.c) };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function cellsIn(rect) {
|
|
20
|
+
const out = [];
|
|
21
|
+
for (let r = rect.top; r <= rect.bottom; r++) {
|
|
22
|
+
for (let c = rect.left; c <= rect.right; c++) out.push({ r: r, c: c });
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isSingle(a, f) { return a.r === f.r && a.c === f.c; }
|
|
28
|
+
|
|
29
|
+
const DELTA = { up: [-1, 0], down: [1, 0], left: [0, -1], right: [0, 1] };
|
|
30
|
+
function step(p, dir, rowCount, colCount) {
|
|
31
|
+
const d = DELTA[dir];
|
|
32
|
+
return { r: clamp(p.r + d[0], 0, rowCount - 1), c: clamp(p.c + d[1], 0, colCount - 1) };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Excel's TSV: TAB between fields, newline between records, a field that
|
|
36
|
+
// STARTS with a quote is quoted ("" inside is one quote, and it may hold
|
|
37
|
+
// TABs and newlines). A trailing newline does not make an empty record.
|
|
38
|
+
function parseTsv(text) {
|
|
39
|
+
const s = String(text).replace(/\r\n?/g, '\n');
|
|
40
|
+
const rows = [];
|
|
41
|
+
let row = [];
|
|
42
|
+
let field = '';
|
|
43
|
+
let atStart = true;
|
|
44
|
+
let i = 0;
|
|
45
|
+
while (i < s.length) {
|
|
46
|
+
const ch = s[i];
|
|
47
|
+
if (atStart && ch === '"') {
|
|
48
|
+
i++;
|
|
49
|
+
while (i < s.length) {
|
|
50
|
+
if (s[i] === '"') {
|
|
51
|
+
if (s[i + 1] === '"') { field += '"'; i += 2; continue; }
|
|
52
|
+
i++;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
field += s[i];
|
|
56
|
+
i++;
|
|
57
|
+
}
|
|
58
|
+
atStart = false;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (ch === '\t') { row.push(field); field = ''; atStart = true; i++; continue; }
|
|
62
|
+
if (ch === '\n') { row.push(field); rows.push(row); row = []; field = ''; atStart = true; i++; continue; }
|
|
63
|
+
field += ch;
|
|
64
|
+
atStart = false;
|
|
65
|
+
i++;
|
|
66
|
+
}
|
|
67
|
+
if (field !== '' || row.length) { row.push(field); rows.push(row); }
|
|
68
|
+
return rows;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function quoteField(f) {
|
|
72
|
+
return /[\t\n"]/.test(f) ? '"' + f.replace(/"/g, '""') + '"' : f;
|
|
73
|
+
}
|
|
74
|
+
function toTsv(grid) {
|
|
75
|
+
return grid.map((row) => row.map(quoteField).join('\t')).join('\n');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function growthNeeded(origin, grid, rowCount, colCount) {
|
|
79
|
+
const width = grid.reduce((w, row) => Math.max(w, row.length), 0);
|
|
80
|
+
return { addRows: Math.max(0, origin.r + grid.length - rowCount),
|
|
81
|
+
addCols: Math.max(0, origin.c + width - colCount) };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { normalize, cellsIn, isSingle, step, parseTsv, toTsv, growthNeeded };
|
|
85
|
+
});
|
package/lib/editor/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ const AUTO_CLOSE_MESSAGES = {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
async function startEditSession({
|
|
19
|
-
files, port = null, open = true, quiet = false,
|
|
19
|
+
files, port = null, open = true, quiet = false, connectionGraceMs, neverOpenedGraceMs,
|
|
20
20
|
}) {
|
|
21
21
|
const srv = await createEditorServer({
|
|
22
22
|
files,
|
|
@@ -25,7 +25,7 @@ async function startEditSession({
|
|
|
25
25
|
// Included when the caller supplies a value; otherwise omitted so
|
|
26
26
|
// createEditorServer's own default applies, rather than a duplicated
|
|
27
27
|
// default here that could drift out of sync with it.
|
|
28
|
-
...(
|
|
28
|
+
...(connectionGraceMs === undefined ? {} : { connectionGraceMs }),
|
|
29
29
|
...(neverOpenedGraceMs === undefined ? {} : { neverOpenedGraceMs }),
|
|
30
30
|
});
|
|
31
31
|
const urls = files.map((f) => srv.urlFor(f));
|