@devyrpauli/mddocs 0.1.0 → 0.2.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/README.md +39 -29
- package/cli.mjs +710 -70
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,8 +49,8 @@ server. `mddocs` takes a different approach:
|
|
|
49
49
|
|---|---|
|
|
50
50
|
| Browser editor (comments, suggestions, provenance) | `mddocs open <file>` (single-user) or `mddocs serve <file>` (multiplayer) |
|
|
51
51
|
| Real-time multiplayer with presence | `mddocs serve <file>`: everyone on the URL co-edits live; edits persist to the file plus git |
|
|
52
|
-
| Role-based share links (editor / commenter / viewer) | `serve` prints a link per role; viewers
|
|
53
|
-
| Agent HTTP API | AI tools read state
|
|
52
|
+
| Role-based share links (editor / commenter / viewer) | `serve` prints a link per role; roles enforced server-side (viewers read-only, commenters cannot edit prose) |
|
|
53
|
+
| Agent HTTP API | AI tools read state, post comments/suggestions, or rewrite prose live, attributed to `ai:<model>` |
|
|
54
54
|
| Comments and suggestions from the terminal | `mddocs comment ...`, `mddocs suggest ...`, `mddocs accept`/`reject` |
|
|
55
55
|
| History and diff | `mddocs log <file>`, `mddocs diff <file> [rev]` (plain git underneath) |
|
|
56
56
|
| Async multiplayer and conflict resolution | edit on branches; `mddocs resolve <file>` unions a conflicted PROOF footer |
|
|
@@ -129,23 +129,38 @@ grant:
|
|
|
129
129
|
|
|
130
130
|
- An absent or unknown token gets the least privilege (viewer), so a leaked bare
|
|
131
131
|
URL cannot edit.
|
|
132
|
-
-
|
|
133
|
-
so a crafted client
|
|
134
|
-
|
|
132
|
+
- Roles are enforced server-side, not just in the editor UI. A viewer's WebSocket
|
|
133
|
+
connection is read-only, so a crafted client cannot write at all. A commenter
|
|
134
|
+
may write comments (a comment is a write to the marks map) but cannot edit the
|
|
135
|
+
prose: any prose change from a commenter connection is reverted server-side
|
|
136
|
+
before it persists or reaches other clients. Editors can do both.
|
|
135
137
|
|
|
136
138
|
## Agent HTTP API
|
|
137
139
|
|
|
138
140
|
A live `serve` session also exposes an HTTP API so AI agents can read the
|
|
139
|
-
document
|
|
140
|
-
real time and
|
|
141
|
-
URL and an agent token; send it as the
|
|
141
|
+
document, post comments/suggestions, and edit the prose directly. Everything
|
|
142
|
+
appears in every connected editor in real time and persists to git, attributed to
|
|
143
|
+
`ai:<model>`. `serve` prints the base URL and an agent token; send it as the
|
|
144
|
+
`x-share-token` header.
|
|
142
145
|
|
|
143
146
|
```
|
|
144
147
|
GET /api/agent/:slug/state -> { content, marks }
|
|
145
148
|
POST /api/agent/:slug/comment { quote, text, model? } -> { id }
|
|
146
149
|
POST /api/agent/:slug/suggest { quote, replace|insert|delete, model? } -> { id, kind }
|
|
150
|
+
POST /api/agent/:slug/rewrite { markdown, quote?, model? } -> { chars, by, markId? }
|
|
147
151
|
```
|
|
148
152
|
|
|
153
|
+
`suggest` proposes a change a human accepts; `rewrite` edits the prose directly.
|
|
154
|
+
With a `quote`, `rewrite` replaces that span; without one it replaces the whole
|
|
155
|
+
body. The change is applied to the live document and recorded as an authored mark.
|
|
156
|
+
|
|
157
|
+
By default `serve` issues one shared agent token. Pass `--agent <name>` (repeatable)
|
|
158
|
+
to register named agents, each with its own token; `serve` then prints a token per
|
|
159
|
+
agent. A request that omits `model` is attributed to the token's agent name
|
|
160
|
+
(`ai:<name>`). Per-agent rate limits are available through the engine API
|
|
161
|
+
(`serveShare({ agents: [{ name, rateLimit: { maxRequests, windowMs } }] })`),
|
|
162
|
+
returning HTTP 429 when exceeded.
|
|
163
|
+
|
|
149
164
|
```bash
|
|
150
165
|
# Read the live document:
|
|
151
166
|
curl -H "x-share-token: $TOKEN" http://127.0.0.1:<port>/api/agent/notes.md/state
|
|
@@ -175,28 +190,30 @@ git add notes.md && git commit
|
|
|
175
190
|
|
|
176
191
|
```
|
|
177
192
|
mddocs open <file> [--port <n>] [--no-autocommit] single-user browser editor (loopback)
|
|
178
|
-
mddocs serve <file> [--port <n>] [--host <ip>] [--no-autocommit]
|
|
193
|
+
mddocs serve <file> [--port <n>] [--host <ip>] [--no-autocommit] [--agent <name>]
|
|
179
194
|
live multiplayer, role links, agent API
|
|
180
195
|
mddocs init mark the repo as mddocs-managed
|
|
181
196
|
mddocs resolve <file> union a git-conflicted PROOF footer
|
|
182
197
|
|
|
183
198
|
mddocs comment add <file> --quote <q> --text <t> add a comment anchored to <q>
|
|
184
199
|
mddocs comment ls <file> [--open|--resolved|--orphaned]
|
|
185
|
-
mddocs comment reply <id> --text <t> --file <f>
|
|
186
|
-
mddocs comment resolve <id> --file <f>
|
|
200
|
+
mddocs comment reply <id> --text <t> [--file <f>] reply in a comment thread
|
|
201
|
+
mddocs comment resolve <id> [--file <f>] resolve a comment thread
|
|
187
202
|
|
|
188
203
|
mddocs suggest <file> --quote <q> (--replace <c> | --insert <c> | --delete)
|
|
189
|
-
mddocs accept <id> --file <f>
|
|
190
|
-
mddocs reject <id> --file <f>
|
|
204
|
+
mddocs accept <id> [--file <f>] apply a suggestion to the prose
|
|
205
|
+
mddocs reject <id> [--file <f>] mark a suggestion rejected
|
|
191
206
|
|
|
192
207
|
mddocs log <file> commit history for a document
|
|
193
208
|
mddocs diff <file> [rev] changes vs working tree or a revision
|
|
194
209
|
```
|
|
195
210
|
|
|
196
|
-
Notes. Id-only commands (`reply`, `resolve`, `accept`, `reject`)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
211
|
+
Notes. Id-only commands (`reply`, `resolve`, `accept`, `reject`) find their
|
|
212
|
+
document automatically by scanning the managed `.md` files for the mark; pass
|
|
213
|
+
`--file <path>` to skip the scan or disambiguate. `accept` applies the suggested
|
|
214
|
+
change to the prose (replace, insert, or delete, anchored by the suggestion's
|
|
215
|
+
quote) and consumes the suggestion; `reject` records the decision on the mark and
|
|
216
|
+
leaves the prose unchanged.
|
|
200
217
|
|
|
201
218
|
## Architecture
|
|
202
219
|
|
|
@@ -259,24 +276,17 @@ browser-interactive path is verified manually.
|
|
|
259
276
|
|
|
260
277
|
- M1: local-first editor and CLI (comments, suggestions, provenance, git history). Done.
|
|
261
278
|
- M2: live collaboration server (real-time multiplayer, file plus git canonical). Done.
|
|
262
|
-
- M2.5: share links and roles (editor/commenter/viewer, server-side
|
|
263
|
-
|
|
279
|
+
- M2.5: share links and roles (editor/commenter/viewer, server-side role
|
|
280
|
+
enforcement: viewers read-only, commenters cannot edit prose). Done.
|
|
281
|
+
- M3: agent HTTP API (read state, comment, suggest, and rewrite prose live). Done.
|
|
264
282
|
|
|
265
283
|
## Upcoming updates
|
|
266
284
|
|
|
267
285
|
Contributions welcome. Next on the list:
|
|
268
286
|
|
|
269
|
-
- Agent direct-rewrite endpoint: let agents edit prose directly, not just propose
|
|
270
|
-
(v1 is propose-only; humans accept).
|
|
271
|
-
- Per-agent identity tokens and rate limiting, instead of one shared agent token.
|
|
272
|
-
- Commenter-granularity enforcement: viewers are enforced server-side; enforce the
|
|
273
|
-
comment-vs-edit split on the wire too.
|
|
274
|
-
- CLI `accept` applies the prose rewrite (today it records the decision; applying
|
|
275
|
-
the edit to the body is editor-only).
|
|
276
|
-
- Global mark-to-file index, so id-only commands no longer need an explicit `--file`.
|
|
277
287
|
- Presence and events for agents.
|
|
278
|
-
- Publish
|
|
279
|
-
|
|
288
|
+
- Publish under a real, unscoped npm project name (currently the `@devyrpauli`
|
|
289
|
+
scope while the name is settled).
|
|
280
290
|
- Upstream the `@proof/core` TS2308 fix
|
|
281
291
|
([proof-sdk#57](https://github.com/EveryInc/proof-sdk/pull/57)) and drop the
|
|
282
292
|
local fork patch once merged.
|
package/cli.mjs
CHANGED
|
@@ -4166,8 +4166,8 @@ var require_lib = __commonJS({
|
|
|
4166
4166
|
if (typeof cb !== "function") {
|
|
4167
4167
|
opts = cb;
|
|
4168
4168
|
cb = null;
|
|
4169
|
-
deferred2 = new this.Promise(function(
|
|
4170
|
-
deferredResolve =
|
|
4169
|
+
deferred2 = new this.Promise(function(resolve4, reject) {
|
|
4170
|
+
deferredResolve = resolve4;
|
|
4171
4171
|
deferredReject = reject;
|
|
4172
4172
|
});
|
|
4173
4173
|
}
|
|
@@ -4315,17 +4315,17 @@ var require_lib = __commonJS({
|
|
|
4315
4315
|
if (typeof cb === "function") {
|
|
4316
4316
|
fnx(cb);
|
|
4317
4317
|
} else {
|
|
4318
|
-
return new this.Promise(function(
|
|
4318
|
+
return new this.Promise(function(resolve4, reject) {
|
|
4319
4319
|
if (fnx.length === 1) {
|
|
4320
4320
|
fnx(function(err, ret) {
|
|
4321
4321
|
if (err) {
|
|
4322
4322
|
reject(err);
|
|
4323
4323
|
} else {
|
|
4324
|
-
|
|
4324
|
+
resolve4(ret);
|
|
4325
4325
|
}
|
|
4326
4326
|
});
|
|
4327
4327
|
} else {
|
|
4328
|
-
|
|
4328
|
+
resolve4(fnx());
|
|
4329
4329
|
}
|
|
4330
4330
|
});
|
|
4331
4331
|
}
|
|
@@ -11708,7 +11708,7 @@ function canAppendWithSubstitutedLinebreaks(a, b2) {
|
|
|
11708
11708
|
function joinable2(a, b2) {
|
|
11709
11709
|
return !!(a && b2 && !a.isLeaf && canAppendWithSubstitutedLinebreaks(a, b2));
|
|
11710
11710
|
}
|
|
11711
|
-
function
|
|
11711
|
+
function join3(tr, pos, depth) {
|
|
11712
11712
|
let convertNewlines = null;
|
|
11713
11713
|
let { linebreakReplacement } = tr.doc.type.schema;
|
|
11714
11714
|
let $before = tr.doc.resolve(pos - depth), beforeType = $before.node().type;
|
|
@@ -13095,7 +13095,7 @@ var init_dist3 = __esm({
|
|
|
13095
13095
|
last and first siblings are also joined, and so on.
|
|
13096
13096
|
*/
|
|
13097
13097
|
join(pos, depth = 1) {
|
|
13098
|
-
|
|
13098
|
+
join3(this, pos, depth);
|
|
13099
13099
|
return this;
|
|
13100
13100
|
}
|
|
13101
13101
|
/**
|
|
@@ -19733,14 +19733,14 @@ var init_lib2 = __esm({
|
|
|
19733
19733
|
this.#listener = null;
|
|
19734
19734
|
this.#status = "pending";
|
|
19735
19735
|
this.start = () => {
|
|
19736
|
-
this.#promise ??= new Promise((
|
|
19736
|
+
this.#promise ??= new Promise((resolve4, reject) => {
|
|
19737
19737
|
this.#listener = (e) => {
|
|
19738
19738
|
if (!(e instanceof CustomEvent)) return;
|
|
19739
19739
|
if (e.detail.id === this.#eventUniqId) {
|
|
19740
19740
|
this.#status = "resolved";
|
|
19741
19741
|
this.#removeListener();
|
|
19742
19742
|
e.stopImmediatePropagation();
|
|
19743
|
-
|
|
19743
|
+
resolve4();
|
|
19744
19744
|
}
|
|
19745
19745
|
};
|
|
19746
19746
|
this.#waitTimeout(() => {
|
|
@@ -22430,10 +22430,10 @@ function resolveAll(constructs2, events, context) {
|
|
|
22430
22430
|
const called = [];
|
|
22431
22431
|
let index2 = -1;
|
|
22432
22432
|
while (++index2 < constructs2.length) {
|
|
22433
|
-
const
|
|
22434
|
-
if (
|
|
22435
|
-
events =
|
|
22436
|
-
called.push(
|
|
22433
|
+
const resolve4 = constructs2[index2].resolveAll;
|
|
22434
|
+
if (resolve4 && !called.includes(resolve4)) {
|
|
22435
|
+
events = resolve4(events, context);
|
|
22436
|
+
called.push(resolve4);
|
|
22437
22437
|
}
|
|
22438
22438
|
}
|
|
22439
22439
|
return events;
|
|
@@ -28608,12 +28608,12 @@ function joinDefaults(left, right, parent, state) {
|
|
|
28608
28608
|
return parent.spread ? 1 : 0;
|
|
28609
28609
|
}
|
|
28610
28610
|
}
|
|
28611
|
-
var
|
|
28611
|
+
var join4;
|
|
28612
28612
|
var init_join = __esm({
|
|
28613
28613
|
"node_modules/mdast-util-to-markdown/lib/join.js"() {
|
|
28614
28614
|
init_format_code_as_indented();
|
|
28615
28615
|
init_format_heading_as_setext();
|
|
28616
|
-
|
|
28616
|
+
join4 = [joinDefaults];
|
|
28617
28617
|
}
|
|
28618
28618
|
});
|
|
28619
28619
|
|
|
@@ -29060,7 +29060,7 @@ function toMarkdown(tree, options) {
|
|
|
29060
29060
|
handle: void 0,
|
|
29061
29061
|
indentLines,
|
|
29062
29062
|
indexStack: [],
|
|
29063
|
-
join: [...
|
|
29063
|
+
join: [...join4],
|
|
29064
29064
|
options: {},
|
|
29065
29065
|
safe: safeBound,
|
|
29066
29066
|
stack: [],
|
|
@@ -30357,7 +30357,7 @@ var init_lib16 = __esm({
|
|
|
30357
30357
|
assertParser("process", this.parser || this.Parser);
|
|
30358
30358
|
assertCompiler("process", this.compiler || this.Compiler);
|
|
30359
30359
|
return done ? executor(void 0, done) : new Promise(executor);
|
|
30360
|
-
function executor(
|
|
30360
|
+
function executor(resolve4, reject) {
|
|
30361
30361
|
const realFile = vfile(file);
|
|
30362
30362
|
const parseTree = (
|
|
30363
30363
|
/** @type {HeadTree extends undefined ? Node : HeadTree} */
|
|
@@ -30388,8 +30388,8 @@ var init_lib16 = __esm({
|
|
|
30388
30388
|
function realDone(error, file2) {
|
|
30389
30389
|
if (error || !file2) {
|
|
30390
30390
|
reject(error);
|
|
30391
|
-
} else if (
|
|
30392
|
-
|
|
30391
|
+
} else if (resolve4) {
|
|
30392
|
+
resolve4(file2);
|
|
30393
30393
|
} else {
|
|
30394
30394
|
ok2(done, "`done` is defined if `resolve` is not");
|
|
30395
30395
|
done(void 0, file2);
|
|
@@ -30491,7 +30491,7 @@ var init_lib16 = __esm({
|
|
|
30491
30491
|
file = void 0;
|
|
30492
30492
|
}
|
|
30493
30493
|
return done ? executor(void 0, done) : new Promise(executor);
|
|
30494
|
-
function executor(
|
|
30494
|
+
function executor(resolve4, reject) {
|
|
30495
30495
|
ok2(
|
|
30496
30496
|
typeof file !== "function",
|
|
30497
30497
|
"`file` can\u2019t be a `done` anymore, we checked"
|
|
@@ -30505,8 +30505,8 @@ var init_lib16 = __esm({
|
|
|
30505
30505
|
);
|
|
30506
30506
|
if (error) {
|
|
30507
30507
|
reject(error);
|
|
30508
|
-
} else if (
|
|
30509
|
-
|
|
30508
|
+
} else if (resolve4) {
|
|
30509
|
+
resolve4(resultingTree);
|
|
30510
30510
|
} else {
|
|
30511
30511
|
ok2(done, "`done` is defined if `resolve` is not");
|
|
30512
30512
|
done(void 0, resultingTree, file2);
|
|
@@ -32846,9 +32846,9 @@ var init_lib19 = __esm({
|
|
|
32846
32846
|
this.remove = async (plugins3) => {
|
|
32847
32847
|
if (this.#status === EditorStatus.OnCreate) {
|
|
32848
32848
|
console.warn("[Milkdown]: You are trying to remove plugins when the editor is creating, this is not recommended, please check your code.");
|
|
32849
|
-
return new Promise((
|
|
32849
|
+
return new Promise((resolve4) => {
|
|
32850
32850
|
setTimeout(() => {
|
|
32851
|
-
|
|
32851
|
+
resolve4(this.remove(plugins3));
|
|
32852
32852
|
}, 50);
|
|
32853
32853
|
});
|
|
32854
32854
|
}
|
|
@@ -32867,9 +32867,9 @@ var init_lib19 = __esm({
|
|
|
32867
32867
|
};
|
|
32868
32868
|
this.destroy = async (clearPlugins = false) => {
|
|
32869
32869
|
if (this.#status === EditorStatus.Destroyed || this.#status === EditorStatus.OnDestroy) return this;
|
|
32870
|
-
if (this.#status === EditorStatus.OnCreate) return new Promise((
|
|
32870
|
+
if (this.#status === EditorStatus.OnCreate) return new Promise((resolve4) => {
|
|
32871
32871
|
setTimeout(() => {
|
|
32872
|
-
|
|
32872
|
+
resolve4(this.destroy(clearPlugins));
|
|
32873
32873
|
}, 50);
|
|
32874
32874
|
});
|
|
32875
32875
|
if (clearPlugins) this.#configureList = [];
|
|
@@ -42630,6 +42630,28 @@ function reanchorMarks(content3, marks) {
|
|
|
42630
42630
|
return { marks: out, orphaned };
|
|
42631
42631
|
}
|
|
42632
42632
|
|
|
42633
|
+
// packages/mddocs-local/src/apply.ts
|
|
42634
|
+
function applySuggestion(content3, mark) {
|
|
42635
|
+
const kind = mark.kind;
|
|
42636
|
+
if (kind !== "insert" && kind !== "delete" && kind !== "replace") {
|
|
42637
|
+
throw new Error(`mark ${mark.id} is a ${kind}, not a suggestion`);
|
|
42638
|
+
}
|
|
42639
|
+
const span = resolveQuote(content3, mark.quote);
|
|
42640
|
+
if (!span) {
|
|
42641
|
+
throw new Error(`cannot apply suggestion ${mark.id}: quoted text not found`);
|
|
42642
|
+
}
|
|
42643
|
+
const { from: from3, to } = span;
|
|
42644
|
+
const replacement = mark.data?.content ?? "";
|
|
42645
|
+
switch (kind) {
|
|
42646
|
+
case "replace":
|
|
42647
|
+
return content3.slice(0, from3) + replacement + content3.slice(to);
|
|
42648
|
+
case "delete":
|
|
42649
|
+
return content3.slice(0, from3) + content3.slice(to);
|
|
42650
|
+
case "insert":
|
|
42651
|
+
return content3.slice(0, to) + replacement + content3.slice(to);
|
|
42652
|
+
}
|
|
42653
|
+
}
|
|
42654
|
+
|
|
42633
42655
|
// packages/mddocs-local/src/footer.ts
|
|
42634
42656
|
var CONFLICT_RE = /^<{7} .*$[\s\S]*?^>{7} .*$/m;
|
|
42635
42657
|
var OURS_MARKER = /^<{7} .*$\n?/m;
|
|
@@ -47658,11 +47680,58 @@ async function diff(path2, rev) {
|
|
|
47658
47680
|
return g.diff(args2);
|
|
47659
47681
|
}
|
|
47660
47682
|
|
|
47683
|
+
// packages/mddocs-local/src/markindex.ts
|
|
47684
|
+
import { readdir } from "node:fs/promises";
|
|
47685
|
+
import { join, resolve } from "node:path";
|
|
47686
|
+
async function listManagedDocs(cwd) {
|
|
47687
|
+
if (await isGitRepo(cwd)) {
|
|
47688
|
+
const root2 = (await esm_default(cwd).revparse(["--show-toplevel"])).trim();
|
|
47689
|
+
const out = await esm_default(root2).raw([
|
|
47690
|
+
"ls-files",
|
|
47691
|
+
"--cached",
|
|
47692
|
+
"--others",
|
|
47693
|
+
"--exclude-standard",
|
|
47694
|
+
"--",
|
|
47695
|
+
"*.md"
|
|
47696
|
+
]);
|
|
47697
|
+
return out.split("\n").filter(Boolean).map((p2) => resolve(root2, p2));
|
|
47698
|
+
}
|
|
47699
|
+
return walkMd(cwd);
|
|
47700
|
+
}
|
|
47701
|
+
async function walkMd(dir) {
|
|
47702
|
+
const out = [];
|
|
47703
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
47704
|
+
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
47705
|
+
const full = join(dir, entry.name);
|
|
47706
|
+
if (entry.isDirectory()) out.push(...await walkMd(full));
|
|
47707
|
+
else if (entry.isFile() && entry.name.endsWith(".md")) out.push(full);
|
|
47708
|
+
}
|
|
47709
|
+
return out;
|
|
47710
|
+
}
|
|
47711
|
+
async function buildMarkIndex(cwd) {
|
|
47712
|
+
const index2 = /* @__PURE__ */ new Map();
|
|
47713
|
+
for (const file of await listManagedDocs(cwd)) {
|
|
47714
|
+
let marks;
|
|
47715
|
+
try {
|
|
47716
|
+
marks = (await loadDoc(file)).marks;
|
|
47717
|
+
} catch {
|
|
47718
|
+
continue;
|
|
47719
|
+
}
|
|
47720
|
+
for (const id3 of Object.keys(marks)) {
|
|
47721
|
+
if (!index2.has(id3)) index2.set(id3, file);
|
|
47722
|
+
}
|
|
47723
|
+
}
|
|
47724
|
+
return index2;
|
|
47725
|
+
}
|
|
47726
|
+
async function findFileForMark(id3, cwd) {
|
|
47727
|
+
return (await buildMarkIndex(cwd)).get(id3);
|
|
47728
|
+
}
|
|
47729
|
+
|
|
47661
47730
|
// packages/mddocs-local/src/serve.ts
|
|
47662
47731
|
import { createServer } from "node:http";
|
|
47663
47732
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
47664
47733
|
import { fileURLToPath } from "node:url";
|
|
47665
|
-
import { basename, dirname as dirname2, join, normalize as normalize2, resolve } from "node:path";
|
|
47734
|
+
import { basename, dirname as dirname2, join as join2, normalize as normalize2, resolve as resolve2 } from "node:path";
|
|
47666
47735
|
import { existsSync } from "node:fs";
|
|
47667
47736
|
async function readRaw(path2) {
|
|
47668
47737
|
try {
|
|
@@ -47698,7 +47767,7 @@ async function createSession(path2, opts = {}) {
|
|
|
47698
47767
|
}
|
|
47699
47768
|
};
|
|
47700
47769
|
}
|
|
47701
|
-
var DEFAULT_DIST = process.env.MDDOCS_DIST ??
|
|
47770
|
+
var DEFAULT_DIST = process.env.MDDOCS_DIST ?? resolve2(dirname2(fileURLToPath(import.meta.url)), "../../../dist");
|
|
47702
47771
|
var CONTENT_TYPES = {
|
|
47703
47772
|
".html": "text/html; charset=utf-8",
|
|
47704
47773
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -47731,10 +47800,10 @@ function sendJson(res, status, body) {
|
|
|
47731
47800
|
async function serve(path2, opts = {}) {
|
|
47732
47801
|
const session = await createSession(path2, opts);
|
|
47733
47802
|
const distDir = opts.distDir ?? DEFAULT_DIST;
|
|
47734
|
-
const absPath =
|
|
47803
|
+
const absPath = resolve2(path2);
|
|
47735
47804
|
async function serveStatic(urlPath, res) {
|
|
47736
47805
|
const rel = urlPath === "/" ? "index.html" : urlPath.replace(/^\/+/, "");
|
|
47737
|
-
const target = normalize2(
|
|
47806
|
+
const target = normalize2(join2(distDir, rel));
|
|
47738
47807
|
if (target !== distDir && !target.startsWith(distDir + "/")) {
|
|
47739
47808
|
res.writeHead(403).end("Forbidden");
|
|
47740
47809
|
return;
|
|
@@ -47967,6 +48036,14 @@ var appendTo = (dest, src) => {
|
|
|
47967
48036
|
}
|
|
47968
48037
|
};
|
|
47969
48038
|
var from = Array.from;
|
|
48039
|
+
var some = (arr, f) => {
|
|
48040
|
+
for (let i2 = 0; i2 < arr.length; i2++) {
|
|
48041
|
+
if (f(arr[i2], i2, arr)) {
|
|
48042
|
+
return true;
|
|
48043
|
+
}
|
|
48044
|
+
}
|
|
48045
|
+
return false;
|
|
48046
|
+
};
|
|
47970
48047
|
var isArray = Array.isArray;
|
|
47971
48048
|
|
|
47972
48049
|
// node_modules/lib0/observable.js
|
|
@@ -49245,17 +49322,17 @@ var Doc = class _Doc extends ObservableV2 {
|
|
|
49245
49322
|
this.isLoaded = false;
|
|
49246
49323
|
this.isSynced = false;
|
|
49247
49324
|
this.isDestroyed = false;
|
|
49248
|
-
this.whenLoaded = create5((
|
|
49325
|
+
this.whenLoaded = create5((resolve4) => {
|
|
49249
49326
|
this.on("load", () => {
|
|
49250
49327
|
this.isLoaded = true;
|
|
49251
|
-
|
|
49328
|
+
resolve4(this);
|
|
49252
49329
|
});
|
|
49253
49330
|
});
|
|
49254
|
-
const provideSyncedPromise = () => create5((
|
|
49331
|
+
const provideSyncedPromise = () => create5((resolve4) => {
|
|
49255
49332
|
const eventHandler = (isSynced) => {
|
|
49256
49333
|
if (isSynced === void 0 || isSynced === true) {
|
|
49257
49334
|
this.off("sync", eventHandler);
|
|
49258
|
-
|
|
49335
|
+
resolve4();
|
|
49259
49336
|
}
|
|
49260
49337
|
};
|
|
49261
49338
|
this.on("sync", eventHandler);
|
|
@@ -50327,6 +50404,16 @@ var findRootTypeKey = (type2) => {
|
|
|
50327
50404
|
}
|
|
50328
50405
|
throw unexpectedCase();
|
|
50329
50406
|
};
|
|
50407
|
+
var isParentOf = (parent, child) => {
|
|
50408
|
+
while (child !== null) {
|
|
50409
|
+
if (child.parent === parent) {
|
|
50410
|
+
return true;
|
|
50411
|
+
}
|
|
50412
|
+
child = /** @type {AbstractType<any>} */
|
|
50413
|
+
child.parent._item;
|
|
50414
|
+
}
|
|
50415
|
+
return false;
|
|
50416
|
+
};
|
|
50330
50417
|
var Snapshot = class {
|
|
50331
50418
|
/**
|
|
50332
50419
|
* @param {DeleteSet} ds
|
|
@@ -50726,6 +50813,310 @@ var transact = (doc4, f, origin = null, local = true) => {
|
|
|
50726
50813
|
}
|
|
50727
50814
|
return result;
|
|
50728
50815
|
};
|
|
50816
|
+
var StackItem = class {
|
|
50817
|
+
/**
|
|
50818
|
+
* @param {DeleteSet} deletions
|
|
50819
|
+
* @param {DeleteSet} insertions
|
|
50820
|
+
*/
|
|
50821
|
+
constructor(deletions, insertions) {
|
|
50822
|
+
this.insertions = insertions;
|
|
50823
|
+
this.deletions = deletions;
|
|
50824
|
+
this.meta = /* @__PURE__ */ new Map();
|
|
50825
|
+
}
|
|
50826
|
+
};
|
|
50827
|
+
var clearUndoManagerStackItem = (tr, um, stackItem) => {
|
|
50828
|
+
iterateDeletedStructs(tr, stackItem.deletions, (item) => {
|
|
50829
|
+
if (item instanceof Item && um.scope.some((type2) => type2 === tr.doc || isParentOf(
|
|
50830
|
+
/** @type {AbstractType<any>} */
|
|
50831
|
+
type2,
|
|
50832
|
+
item
|
|
50833
|
+
))) {
|
|
50834
|
+
keepItem(item, false);
|
|
50835
|
+
}
|
|
50836
|
+
});
|
|
50837
|
+
};
|
|
50838
|
+
var popStackItem = (undoManager, stack, eventType) => {
|
|
50839
|
+
let _tr = null;
|
|
50840
|
+
const doc4 = undoManager.doc;
|
|
50841
|
+
const scope = undoManager.scope;
|
|
50842
|
+
transact(doc4, (transaction) => {
|
|
50843
|
+
while (stack.length > 0 && undoManager.currStackItem === null) {
|
|
50844
|
+
const store = doc4.store;
|
|
50845
|
+
const stackItem = (
|
|
50846
|
+
/** @type {StackItem} */
|
|
50847
|
+
stack.pop()
|
|
50848
|
+
);
|
|
50849
|
+
const itemsToRedo = /* @__PURE__ */ new Set();
|
|
50850
|
+
const itemsToDelete = [];
|
|
50851
|
+
let performedChange = false;
|
|
50852
|
+
iterateDeletedStructs(transaction, stackItem.insertions, (struct) => {
|
|
50853
|
+
if (struct instanceof Item) {
|
|
50854
|
+
if (struct.redone !== null) {
|
|
50855
|
+
let { item, diff: diff2 } = followRedone(store, struct.id);
|
|
50856
|
+
if (diff2 > 0) {
|
|
50857
|
+
item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + diff2));
|
|
50858
|
+
}
|
|
50859
|
+
struct = item;
|
|
50860
|
+
}
|
|
50861
|
+
if (!struct.deleted && scope.some((type2) => type2 === transaction.doc || isParentOf(
|
|
50862
|
+
/** @type {AbstractType<any>} */
|
|
50863
|
+
type2,
|
|
50864
|
+
/** @type {Item} */
|
|
50865
|
+
struct
|
|
50866
|
+
))) {
|
|
50867
|
+
itemsToDelete.push(struct);
|
|
50868
|
+
}
|
|
50869
|
+
}
|
|
50870
|
+
});
|
|
50871
|
+
iterateDeletedStructs(transaction, stackItem.deletions, (struct) => {
|
|
50872
|
+
if (struct instanceof Item && scope.some((type2) => type2 === transaction.doc || isParentOf(
|
|
50873
|
+
/** @type {AbstractType<any>} */
|
|
50874
|
+
type2,
|
|
50875
|
+
struct
|
|
50876
|
+
)) && // Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval.
|
|
50877
|
+
!isDeleted(stackItem.insertions, struct.id)) {
|
|
50878
|
+
itemsToRedo.add(struct);
|
|
50879
|
+
}
|
|
50880
|
+
});
|
|
50881
|
+
itemsToRedo.forEach((struct) => {
|
|
50882
|
+
performedChange = redoItem(transaction, struct, itemsToRedo, stackItem.insertions, undoManager.ignoreRemoteMapChanges, undoManager) !== null || performedChange;
|
|
50883
|
+
});
|
|
50884
|
+
for (let i2 = itemsToDelete.length - 1; i2 >= 0; i2--) {
|
|
50885
|
+
const item = itemsToDelete[i2];
|
|
50886
|
+
if (undoManager.deleteFilter(item)) {
|
|
50887
|
+
item.delete(transaction);
|
|
50888
|
+
performedChange = true;
|
|
50889
|
+
}
|
|
50890
|
+
}
|
|
50891
|
+
undoManager.currStackItem = performedChange ? stackItem : null;
|
|
50892
|
+
}
|
|
50893
|
+
transaction.changed.forEach((subProps, type2) => {
|
|
50894
|
+
if (subProps.has(null) && type2._searchMarker) {
|
|
50895
|
+
type2._searchMarker.length = 0;
|
|
50896
|
+
}
|
|
50897
|
+
});
|
|
50898
|
+
_tr = transaction;
|
|
50899
|
+
}, undoManager);
|
|
50900
|
+
const res = undoManager.currStackItem;
|
|
50901
|
+
if (res != null) {
|
|
50902
|
+
const changedParentTypes = _tr.changedParentTypes;
|
|
50903
|
+
undoManager.emit("stack-item-popped", [{ stackItem: res, type: eventType, changedParentTypes, origin: undoManager }, undoManager]);
|
|
50904
|
+
undoManager.currStackItem = null;
|
|
50905
|
+
}
|
|
50906
|
+
return res;
|
|
50907
|
+
};
|
|
50908
|
+
var UndoManager = class extends ObservableV2 {
|
|
50909
|
+
/**
|
|
50910
|
+
* @param {Doc|AbstractType<any>|Array<AbstractType<any>>} typeScope Limits the scope of the UndoManager. If this is set to a ydoc instance, all changes on that ydoc will be undone. If set to a specific type, only changes on that type or its children will be undone. Also accepts an array of types.
|
|
50911
|
+
* @param {UndoManagerOptions} options
|
|
50912
|
+
*/
|
|
50913
|
+
constructor(typeScope, {
|
|
50914
|
+
captureTimeout = 500,
|
|
50915
|
+
captureTransaction = (_tr) => true,
|
|
50916
|
+
deleteFilter = () => true,
|
|
50917
|
+
trackedOrigins = /* @__PURE__ */ new Set([null]),
|
|
50918
|
+
ignoreRemoteMapChanges = false,
|
|
50919
|
+
doc: doc4 = (
|
|
50920
|
+
/** @type {Doc} */
|
|
50921
|
+
isArray(typeScope) ? typeScope[0].doc : typeScope instanceof Doc ? typeScope : typeScope.doc
|
|
50922
|
+
)
|
|
50923
|
+
} = {}) {
|
|
50924
|
+
super();
|
|
50925
|
+
this.scope = [];
|
|
50926
|
+
this.doc = doc4;
|
|
50927
|
+
this.addToScope(typeScope);
|
|
50928
|
+
this.deleteFilter = deleteFilter;
|
|
50929
|
+
trackedOrigins.add(this);
|
|
50930
|
+
this.trackedOrigins = trackedOrigins;
|
|
50931
|
+
this.captureTransaction = captureTransaction;
|
|
50932
|
+
this.undoStack = [];
|
|
50933
|
+
this.redoStack = [];
|
|
50934
|
+
this.undoing = false;
|
|
50935
|
+
this.redoing = false;
|
|
50936
|
+
this.currStackItem = null;
|
|
50937
|
+
this.lastChange = 0;
|
|
50938
|
+
this.ignoreRemoteMapChanges = ignoreRemoteMapChanges;
|
|
50939
|
+
this.captureTimeout = captureTimeout;
|
|
50940
|
+
this.afterTransactionHandler = (transaction) => {
|
|
50941
|
+
if (!this.captureTransaction(transaction) || !this.scope.some((type2) => transaction.changedParentTypes.has(
|
|
50942
|
+
/** @type {AbstractType<any>} */
|
|
50943
|
+
type2
|
|
50944
|
+
) || type2 === this.doc) || !this.trackedOrigins.has(transaction.origin) && (!transaction.origin || !this.trackedOrigins.has(transaction.origin.constructor))) {
|
|
50945
|
+
return;
|
|
50946
|
+
}
|
|
50947
|
+
const undoing = this.undoing;
|
|
50948
|
+
const redoing = this.redoing;
|
|
50949
|
+
const stack = undoing ? this.redoStack : this.undoStack;
|
|
50950
|
+
if (undoing) {
|
|
50951
|
+
this.stopCapturing();
|
|
50952
|
+
} else if (!redoing) {
|
|
50953
|
+
this.clear(false, true);
|
|
50954
|
+
}
|
|
50955
|
+
const insertions = new DeleteSet();
|
|
50956
|
+
transaction.afterState.forEach((endClock, client) => {
|
|
50957
|
+
const startClock = transaction.beforeState.get(client) || 0;
|
|
50958
|
+
const len = endClock - startClock;
|
|
50959
|
+
if (len > 0) {
|
|
50960
|
+
addToDeleteSet(insertions, client, startClock, len);
|
|
50961
|
+
}
|
|
50962
|
+
});
|
|
50963
|
+
const now = getUnixTime();
|
|
50964
|
+
let didAdd = false;
|
|
50965
|
+
if (this.lastChange > 0 && now - this.lastChange < this.captureTimeout && stack.length > 0 && !undoing && !redoing) {
|
|
50966
|
+
const lastOp = stack[stack.length - 1];
|
|
50967
|
+
lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet]);
|
|
50968
|
+
lastOp.insertions = mergeDeleteSets([lastOp.insertions, insertions]);
|
|
50969
|
+
} else {
|
|
50970
|
+
stack.push(new StackItem(transaction.deleteSet, insertions));
|
|
50971
|
+
didAdd = true;
|
|
50972
|
+
}
|
|
50973
|
+
if (!undoing && !redoing) {
|
|
50974
|
+
this.lastChange = now;
|
|
50975
|
+
}
|
|
50976
|
+
iterateDeletedStructs(
|
|
50977
|
+
transaction,
|
|
50978
|
+
transaction.deleteSet,
|
|
50979
|
+
/** @param {Item|GC} item */
|
|
50980
|
+
(item) => {
|
|
50981
|
+
if (item instanceof Item && this.scope.some((type2) => type2 === transaction.doc || isParentOf(
|
|
50982
|
+
/** @type {AbstractType<any>} */
|
|
50983
|
+
type2,
|
|
50984
|
+
item
|
|
50985
|
+
))) {
|
|
50986
|
+
keepItem(item, true);
|
|
50987
|
+
}
|
|
50988
|
+
}
|
|
50989
|
+
);
|
|
50990
|
+
const changeEvent = [{ stackItem: stack[stack.length - 1], origin: transaction.origin, type: undoing ? "redo" : "undo", changedParentTypes: transaction.changedParentTypes }, this];
|
|
50991
|
+
if (didAdd) {
|
|
50992
|
+
this.emit("stack-item-added", changeEvent);
|
|
50993
|
+
} else {
|
|
50994
|
+
this.emit("stack-item-updated", changeEvent);
|
|
50995
|
+
}
|
|
50996
|
+
};
|
|
50997
|
+
this.doc.on("afterTransaction", this.afterTransactionHandler);
|
|
50998
|
+
this.doc.on("destroy", () => {
|
|
50999
|
+
this.destroy();
|
|
51000
|
+
});
|
|
51001
|
+
}
|
|
51002
|
+
/**
|
|
51003
|
+
* Extend the scope.
|
|
51004
|
+
*
|
|
51005
|
+
* @param {Array<AbstractType<any> | Doc> | AbstractType<any> | Doc} ytypes
|
|
51006
|
+
*/
|
|
51007
|
+
addToScope(ytypes) {
|
|
51008
|
+
const tmpSet = new Set(this.scope);
|
|
51009
|
+
ytypes = isArray(ytypes) ? ytypes : [ytypes];
|
|
51010
|
+
ytypes.forEach((ytype) => {
|
|
51011
|
+
if (!tmpSet.has(ytype)) {
|
|
51012
|
+
tmpSet.add(ytype);
|
|
51013
|
+
if (ytype instanceof AbstractType ? ytype.doc !== this.doc : ytype !== this.doc) warn("[yjs#509] Not same Y.Doc");
|
|
51014
|
+
this.scope.push(ytype);
|
|
51015
|
+
}
|
|
51016
|
+
});
|
|
51017
|
+
}
|
|
51018
|
+
/**
|
|
51019
|
+
* @param {any} origin
|
|
51020
|
+
*/
|
|
51021
|
+
addTrackedOrigin(origin) {
|
|
51022
|
+
this.trackedOrigins.add(origin);
|
|
51023
|
+
}
|
|
51024
|
+
/**
|
|
51025
|
+
* @param {any} origin
|
|
51026
|
+
*/
|
|
51027
|
+
removeTrackedOrigin(origin) {
|
|
51028
|
+
this.trackedOrigins.delete(origin);
|
|
51029
|
+
}
|
|
51030
|
+
clear(clearUndoStack = true, clearRedoStack = true) {
|
|
51031
|
+
if (clearUndoStack && this.canUndo() || clearRedoStack && this.canRedo()) {
|
|
51032
|
+
this.doc.transact((tr) => {
|
|
51033
|
+
if (clearUndoStack) {
|
|
51034
|
+
this.undoStack.forEach((item) => clearUndoManagerStackItem(tr, this, item));
|
|
51035
|
+
this.undoStack = [];
|
|
51036
|
+
}
|
|
51037
|
+
if (clearRedoStack) {
|
|
51038
|
+
this.redoStack.forEach((item) => clearUndoManagerStackItem(tr, this, item));
|
|
51039
|
+
this.redoStack = [];
|
|
51040
|
+
}
|
|
51041
|
+
this.emit("stack-cleared", [{ undoStackCleared: clearUndoStack, redoStackCleared: clearRedoStack }]);
|
|
51042
|
+
});
|
|
51043
|
+
}
|
|
51044
|
+
}
|
|
51045
|
+
/**
|
|
51046
|
+
* UndoManager merges Undo-StackItem if they are created within time-gap
|
|
51047
|
+
* smaller than `options.captureTimeout`. Call `um.stopCapturing()` so that the next
|
|
51048
|
+
* StackItem won't be merged.
|
|
51049
|
+
*
|
|
51050
|
+
*
|
|
51051
|
+
* @example
|
|
51052
|
+
* // without stopCapturing
|
|
51053
|
+
* ytext.insert(0, 'a')
|
|
51054
|
+
* ytext.insert(1, 'b')
|
|
51055
|
+
* um.undo()
|
|
51056
|
+
* ytext.toString() // => '' (note that 'ab' was removed)
|
|
51057
|
+
* // with stopCapturing
|
|
51058
|
+
* ytext.insert(0, 'a')
|
|
51059
|
+
* um.stopCapturing()
|
|
51060
|
+
* ytext.insert(0, 'b')
|
|
51061
|
+
* um.undo()
|
|
51062
|
+
* ytext.toString() // => 'a' (note that only 'b' was removed)
|
|
51063
|
+
*
|
|
51064
|
+
*/
|
|
51065
|
+
stopCapturing() {
|
|
51066
|
+
this.lastChange = 0;
|
|
51067
|
+
}
|
|
51068
|
+
/**
|
|
51069
|
+
* Undo last changes on type.
|
|
51070
|
+
*
|
|
51071
|
+
* @return {StackItem?} Returns StackItem if a change was applied
|
|
51072
|
+
*/
|
|
51073
|
+
undo() {
|
|
51074
|
+
this.undoing = true;
|
|
51075
|
+
let res;
|
|
51076
|
+
try {
|
|
51077
|
+
res = popStackItem(this, this.undoStack, "undo");
|
|
51078
|
+
} finally {
|
|
51079
|
+
this.undoing = false;
|
|
51080
|
+
}
|
|
51081
|
+
return res;
|
|
51082
|
+
}
|
|
51083
|
+
/**
|
|
51084
|
+
* Redo last undo operation.
|
|
51085
|
+
*
|
|
51086
|
+
* @return {StackItem?} Returns StackItem if a change was applied
|
|
51087
|
+
*/
|
|
51088
|
+
redo() {
|
|
51089
|
+
this.redoing = true;
|
|
51090
|
+
let res;
|
|
51091
|
+
try {
|
|
51092
|
+
res = popStackItem(this, this.redoStack, "redo");
|
|
51093
|
+
} finally {
|
|
51094
|
+
this.redoing = false;
|
|
51095
|
+
}
|
|
51096
|
+
return res;
|
|
51097
|
+
}
|
|
51098
|
+
/**
|
|
51099
|
+
* Are undo steps available?
|
|
51100
|
+
*
|
|
51101
|
+
* @return {boolean} `true` if undo is possible
|
|
51102
|
+
*/
|
|
51103
|
+
canUndo() {
|
|
51104
|
+
return this.undoStack.length > 0;
|
|
51105
|
+
}
|
|
51106
|
+
/**
|
|
51107
|
+
* Are redo steps available?
|
|
51108
|
+
*
|
|
51109
|
+
* @return {boolean} `true` if redo is possible
|
|
51110
|
+
*/
|
|
51111
|
+
canRedo() {
|
|
51112
|
+
return this.redoStack.length > 0;
|
|
51113
|
+
}
|
|
51114
|
+
destroy() {
|
|
51115
|
+
this.trackedOrigins.delete(this);
|
|
51116
|
+
this.doc.off("afterTransaction", this.afterTransactionHandler);
|
|
51117
|
+
super.destroy();
|
|
51118
|
+
}
|
|
51119
|
+
};
|
|
50729
51120
|
function* lazyStructReaderGenerator(decoder) {
|
|
50730
51121
|
const numOfStateUpdates = readVarUint(decoder.restDecoder);
|
|
50731
51122
|
for (let i2 = 0; i2 < numOfStateUpdates; i2++) {
|
|
@@ -54998,6 +55389,30 @@ var ContentType = class _ContentType {
|
|
|
54998
55389
|
}
|
|
54999
55390
|
};
|
|
55000
55391
|
var readContentType = (decoder) => new ContentType(typeRefs[decoder.readTypeRef()](decoder));
|
|
55392
|
+
var followRedone = (store, id3) => {
|
|
55393
|
+
let nextID = id3;
|
|
55394
|
+
let diff2 = 0;
|
|
55395
|
+
let item;
|
|
55396
|
+
do {
|
|
55397
|
+
if (diff2 > 0) {
|
|
55398
|
+
nextID = createID(nextID.client, nextID.clock + diff2);
|
|
55399
|
+
}
|
|
55400
|
+
item = getItem(store, nextID);
|
|
55401
|
+
diff2 = nextID.clock - item.id.clock;
|
|
55402
|
+
nextID = item.redone;
|
|
55403
|
+
} while (nextID !== null && item instanceof Item);
|
|
55404
|
+
return {
|
|
55405
|
+
item,
|
|
55406
|
+
diff: diff2
|
|
55407
|
+
};
|
|
55408
|
+
};
|
|
55409
|
+
var keepItem = (item, keep) => {
|
|
55410
|
+
while (item !== null && item.keep !== keep) {
|
|
55411
|
+
item.keep = keep;
|
|
55412
|
+
item = /** @type {AbstractType<any>} */
|
|
55413
|
+
item.parent._item;
|
|
55414
|
+
}
|
|
55415
|
+
};
|
|
55001
55416
|
var splitItem = (transaction, leftItem, diff2) => {
|
|
55002
55417
|
const { client, clock } = leftItem.id;
|
|
55003
55418
|
const rightItem = new Item(
|
|
@@ -55030,6 +55445,105 @@ var splitItem = (transaction, leftItem, diff2) => {
|
|
|
55030
55445
|
leftItem.length = diff2;
|
|
55031
55446
|
return rightItem;
|
|
55032
55447
|
};
|
|
55448
|
+
var isDeletedByUndoStack = (stack, id3) => some(
|
|
55449
|
+
stack,
|
|
55450
|
+
/** @param {StackItem} s */
|
|
55451
|
+
(s) => isDeleted(s.deletions, id3)
|
|
55452
|
+
);
|
|
55453
|
+
var redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) => {
|
|
55454
|
+
const doc4 = transaction.doc;
|
|
55455
|
+
const store = doc4.store;
|
|
55456
|
+
const ownClientID = doc4.clientID;
|
|
55457
|
+
const redone = item.redone;
|
|
55458
|
+
if (redone !== null) {
|
|
55459
|
+
return getItemCleanStart(transaction, redone);
|
|
55460
|
+
}
|
|
55461
|
+
let parentItem = (
|
|
55462
|
+
/** @type {AbstractType<any>} */
|
|
55463
|
+
item.parent._item
|
|
55464
|
+
);
|
|
55465
|
+
let left = null;
|
|
55466
|
+
let right;
|
|
55467
|
+
if (parentItem !== null && parentItem.deleted === true) {
|
|
55468
|
+
if (parentItem.redone === null && (!redoitems.has(parentItem) || redoItem(transaction, parentItem, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) === null)) {
|
|
55469
|
+
return null;
|
|
55470
|
+
}
|
|
55471
|
+
while (parentItem.redone !== null) {
|
|
55472
|
+
parentItem = getItemCleanStart(transaction, parentItem.redone);
|
|
55473
|
+
}
|
|
55474
|
+
}
|
|
55475
|
+
const parentType = parentItem === null ? (
|
|
55476
|
+
/** @type {AbstractType<any>} */
|
|
55477
|
+
item.parent
|
|
55478
|
+
) : (
|
|
55479
|
+
/** @type {ContentType} */
|
|
55480
|
+
parentItem.content.type
|
|
55481
|
+
);
|
|
55482
|
+
if (item.parentSub === null) {
|
|
55483
|
+
left = item.left;
|
|
55484
|
+
right = item;
|
|
55485
|
+
while (left !== null) {
|
|
55486
|
+
let leftTrace = left;
|
|
55487
|
+
while (leftTrace !== null && /** @type {AbstractType<any>} */
|
|
55488
|
+
leftTrace.parent._item !== parentItem) {
|
|
55489
|
+
leftTrace = leftTrace.redone === null ? null : getItemCleanStart(transaction, leftTrace.redone);
|
|
55490
|
+
}
|
|
55491
|
+
if (leftTrace !== null && /** @type {AbstractType<any>} */
|
|
55492
|
+
leftTrace.parent._item === parentItem) {
|
|
55493
|
+
left = leftTrace;
|
|
55494
|
+
break;
|
|
55495
|
+
}
|
|
55496
|
+
left = left.left;
|
|
55497
|
+
}
|
|
55498
|
+
while (right !== null) {
|
|
55499
|
+
let rightTrace = right;
|
|
55500
|
+
while (rightTrace !== null && /** @type {AbstractType<any>} */
|
|
55501
|
+
rightTrace.parent._item !== parentItem) {
|
|
55502
|
+
rightTrace = rightTrace.redone === null ? null : getItemCleanStart(transaction, rightTrace.redone);
|
|
55503
|
+
}
|
|
55504
|
+
if (rightTrace !== null && /** @type {AbstractType<any>} */
|
|
55505
|
+
rightTrace.parent._item === parentItem) {
|
|
55506
|
+
right = rightTrace;
|
|
55507
|
+
break;
|
|
55508
|
+
}
|
|
55509
|
+
right = right.right;
|
|
55510
|
+
}
|
|
55511
|
+
} else {
|
|
55512
|
+
right = null;
|
|
55513
|
+
if (item.right && !ignoreRemoteMapChanges) {
|
|
55514
|
+
left = item;
|
|
55515
|
+
while (left !== null && left.right !== null && (left.right.redone || isDeleted(itemsToDelete, left.right.id) || isDeletedByUndoStack(um.undoStack, left.right.id) || isDeletedByUndoStack(um.redoStack, left.right.id))) {
|
|
55516
|
+
left = left.right;
|
|
55517
|
+
while (left.redone) left = getItemCleanStart(transaction, left.redone);
|
|
55518
|
+
}
|
|
55519
|
+
if (left && left.right !== null) {
|
|
55520
|
+
return null;
|
|
55521
|
+
}
|
|
55522
|
+
} else {
|
|
55523
|
+
left = parentType._map.get(item.parentSub) || null;
|
|
55524
|
+
}
|
|
55525
|
+
if (left !== null && /** @type {AbstractType<any>} */
|
|
55526
|
+
left.parent._item !== parentItem) {
|
|
55527
|
+
left = parentType._map.get(item.parentSub) || null;
|
|
55528
|
+
}
|
|
55529
|
+
}
|
|
55530
|
+
const nextClock = getState(store, ownClientID);
|
|
55531
|
+
const nextId = createID(ownClientID, nextClock);
|
|
55532
|
+
const redoneItem = new Item(
|
|
55533
|
+
nextId,
|
|
55534
|
+
left,
|
|
55535
|
+
left && left.lastId,
|
|
55536
|
+
right,
|
|
55537
|
+
right && right.id,
|
|
55538
|
+
parentType,
|
|
55539
|
+
item.parentSub,
|
|
55540
|
+
item.content.copy()
|
|
55541
|
+
);
|
|
55542
|
+
item.redone = nextId;
|
|
55543
|
+
keepItem(redoneItem, true);
|
|
55544
|
+
redoneItem.integrate(transaction, 0);
|
|
55545
|
+
return redoneItem;
|
|
55546
|
+
};
|
|
55033
55547
|
var Item = class _Item extends AbstractStruct {
|
|
55034
55548
|
/**
|
|
55035
55549
|
* @param {ID} id
|
|
@@ -57401,7 +57915,7 @@ var Hocuspocus = class {
|
|
|
57401
57915
|
process.on("SIGQUIT", signalHandler);
|
|
57402
57916
|
process.on("SIGTERM", signalHandler);
|
|
57403
57917
|
}
|
|
57404
|
-
return new Promise((
|
|
57918
|
+
return new Promise((resolve4, reject) => {
|
|
57405
57919
|
var _a2;
|
|
57406
57920
|
(_a2 = this.server) === null || _a2 === void 0 ? void 0 : _a2.httpServer.listen({
|
|
57407
57921
|
port: this.configuration.port,
|
|
@@ -57417,7 +57931,7 @@ var Hocuspocus = class {
|
|
|
57417
57931
|
};
|
|
57418
57932
|
try {
|
|
57419
57933
|
await this.hooks("onListen", onListenPayload);
|
|
57420
|
-
|
|
57934
|
+
resolve4(this);
|
|
57421
57935
|
} catch (e) {
|
|
57422
57936
|
reject(e);
|
|
57423
57937
|
}
|
|
@@ -57501,19 +58015,19 @@ var Hocuspocus = class {
|
|
|
57501
58015
|
* Destroy the server
|
|
57502
58016
|
*/
|
|
57503
58017
|
async destroy() {
|
|
57504
|
-
await new Promise(async (
|
|
58018
|
+
await new Promise(async (resolve4) => {
|
|
57505
58019
|
var _a2, _b, _c, _d;
|
|
57506
58020
|
(_b = (_a2 = this.server) === null || _a2 === void 0 ? void 0 : _a2.httpServer) === null || _b === void 0 ? void 0 : _b.close();
|
|
57507
58021
|
try {
|
|
57508
58022
|
this.configuration.extensions.push({
|
|
57509
58023
|
async afterUnloadDocument({ instance }) {
|
|
57510
58024
|
if (instance.getDocumentsCount() === 0)
|
|
57511
|
-
|
|
58025
|
+
resolve4("");
|
|
57512
58026
|
}
|
|
57513
58027
|
});
|
|
57514
58028
|
(_d = (_c = this.server) === null || _c === void 0 ? void 0 : _c.webSocketServer) === null || _d === void 0 ? void 0 : _d.close();
|
|
57515
58029
|
if (this.getDocumentsCount() === 0)
|
|
57516
|
-
|
|
58030
|
+
resolve4("");
|
|
57517
58031
|
this.closeConnections();
|
|
57518
58032
|
} catch (error) {
|
|
57519
58033
|
console.error(error);
|
|
@@ -58214,11 +58728,38 @@ async function seedFragmentFromMarkdown(markdown, fragment) {
|
|
|
58214
58728
|
const node2 = parser5.parseMarkdown(markdown);
|
|
58215
58729
|
prosemirrorToYXmlFragment(node2, fragment);
|
|
58216
58730
|
}
|
|
58731
|
+
async function parseMarkdownNode(markdown) {
|
|
58732
|
+
const parser5 = await getParser();
|
|
58733
|
+
return parser5.parseMarkdown(markdown);
|
|
58734
|
+
}
|
|
58735
|
+
function setFragmentFromNode(fragment, node2) {
|
|
58736
|
+
fragment.delete(0, fragment.length);
|
|
58737
|
+
prosemirrorToYXmlFragment(node2, fragment);
|
|
58738
|
+
}
|
|
58217
58739
|
|
|
58218
58740
|
// packages/mddocs-local/src/collab.ts
|
|
58219
58741
|
async function configureCollab(file, opts = {}) {
|
|
58220
58742
|
const slug = opts.slug ?? basename2(file);
|
|
58221
58743
|
const session = await createSession(file, opts);
|
|
58744
|
+
const enforced = /* @__PURE__ */ new WeakSet();
|
|
58745
|
+
function enforceCommenterProse(doc4) {
|
|
58746
|
+
if (enforced.has(doc4)) return;
|
|
58747
|
+
enforced.add(doc4);
|
|
58748
|
+
const trackedOrigins = {
|
|
58749
|
+
has: (o2) => !!o2 && typeof o2 === "object" && o2.context?.role === "commenter",
|
|
58750
|
+
add: () => void 0,
|
|
58751
|
+
delete: () => void 0
|
|
58752
|
+
};
|
|
58753
|
+
const undo = new UndoManager(doc4.getXmlFragment("prosemirror"), {
|
|
58754
|
+
trackedOrigins,
|
|
58755
|
+
captureTimeout: 0
|
|
58756
|
+
});
|
|
58757
|
+
undo.on("stack-item-added", () => {
|
|
58758
|
+
queueMicrotask(() => {
|
|
58759
|
+
if (undo.undoStack.length > 0) undo.undo();
|
|
58760
|
+
});
|
|
58761
|
+
});
|
|
58762
|
+
}
|
|
58222
58763
|
const hocuspocus = new Hocuspocus().configure({
|
|
58223
58764
|
port: opts.port ?? 0,
|
|
58224
58765
|
debounce: opts.storeDebounceMs ?? 150,
|
|
@@ -58237,6 +58778,7 @@ async function configureCollab(file, opts = {}) {
|
|
|
58237
58778
|
for (const [id3, mark] of Object.entries(marks)) {
|
|
58238
58779
|
if (!ymarks.has(id3)) ymarks.set(id3, mark);
|
|
58239
58780
|
}
|
|
58781
|
+
if (opts.authenticate) enforceCommenterProse(data.document);
|
|
58240
58782
|
return data.document;
|
|
58241
58783
|
},
|
|
58242
58784
|
// Persist the settled doc back to the file (+ optional autocommit). The
|
|
@@ -58259,7 +58801,7 @@ async function configureCollab(file, opts = {}) {
|
|
|
58259
58801
|
const verdict = opts.authenticate(data.token);
|
|
58260
58802
|
if (!verdict) throw new Error("Unauthorized");
|
|
58261
58803
|
data.connection.readOnly = verdict.readOnly;
|
|
58262
|
-
return { readOnly: verdict.readOnly };
|
|
58804
|
+
return { readOnly: verdict.readOnly, role: verdict.role };
|
|
58263
58805
|
}
|
|
58264
58806
|
} : {}
|
|
58265
58807
|
});
|
|
@@ -58271,7 +58813,7 @@ import { createServer as createServer3 } from "node:http";
|
|
|
58271
58813
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
58272
58814
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
58273
58815
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
58274
|
-
import { dirname as dirname3, join as
|
|
58816
|
+
import { dirname as dirname3, join as join5, normalize as normalize4, resolve as resolve3 } from "node:path";
|
|
58275
58817
|
|
|
58276
58818
|
// packages/mddocs-local/src/agent.ts
|
|
58277
58819
|
function createAgentApi(hocuspocus, slug, opts = {}) {
|
|
@@ -58307,6 +58849,38 @@ function createAgentApi(hocuspocus, slug, opts = {}) {
|
|
|
58307
58849
|
await inject(mark);
|
|
58308
58850
|
return { id: mark.id, kind: mark.kind };
|
|
58309
58851
|
},
|
|
58852
|
+
// Edit the prose directly (not a proposal): replace a quoted span, or the
|
|
58853
|
+
// whole body when no quote is given. The new markdown is applied to the live
|
|
58854
|
+
// `prosemirror` fragment so it syncs to every editor and persists to the file
|
|
58855
|
+
// (+ git) via onStoreDocument. Authorship is recorded as an `ai:<model>`
|
|
58856
|
+
// authored mark over the new text.
|
|
58857
|
+
async rewrite({ quote, markdown, model }) {
|
|
58858
|
+
const conn = await connect();
|
|
58859
|
+
const doc4 = conn.document;
|
|
58860
|
+
if (!doc4) throw new Error("no live document to rewrite");
|
|
58861
|
+
const current = await fragmentToMarkdown(doc4.getXmlFragment("prosemirror")) ?? "";
|
|
58862
|
+
let next;
|
|
58863
|
+
if (quote && quote.length > 0) {
|
|
58864
|
+
const span = resolveQuote(current, quote);
|
|
58865
|
+
if (!span) throw new Error("quoted text not found in the live document");
|
|
58866
|
+
next = current.slice(0, span.from) + markdown + current.slice(span.to);
|
|
58867
|
+
} else {
|
|
58868
|
+
next = markdown;
|
|
58869
|
+
}
|
|
58870
|
+
const node2 = await parseMarkdownNode(next);
|
|
58871
|
+
await conn.transact((d) => {
|
|
58872
|
+
setFragmentFromNode(d.getXmlFragment("prosemirror"), node2);
|
|
58873
|
+
});
|
|
58874
|
+
const by = actor2(model);
|
|
58875
|
+
const snippet = normalizeQuote(markdown).slice(0, 200);
|
|
58876
|
+
let markId;
|
|
58877
|
+
if (snippet.length > 0) {
|
|
58878
|
+
const mark = createAuthored(by, { from: 0, to: 0 }, snippet);
|
|
58879
|
+
await inject(mark);
|
|
58880
|
+
markId = mark.id;
|
|
58881
|
+
}
|
|
58882
|
+
return { chars: next.length, by, markId };
|
|
58883
|
+
},
|
|
58310
58884
|
async stop() {
|
|
58311
58885
|
if (connPromise) {
|
|
58312
58886
|
const conn = await connPromise;
|
|
@@ -58323,7 +58897,7 @@ var CAPABILITIES = {
|
|
|
58323
58897
|
commenter: { canRead: true, canComment: true, canEdit: false },
|
|
58324
58898
|
viewer: { canRead: true, canComment: false, canEdit: false }
|
|
58325
58899
|
};
|
|
58326
|
-
var DEFAULT_DIST2 = process.env.MDDOCS_DIST ??
|
|
58900
|
+
var DEFAULT_DIST2 = process.env.MDDOCS_DIST ?? resolve3(dirname3(fileURLToPath3(import.meta.url)), "../../../dist");
|
|
58327
58901
|
var CONTENT_TYPES2 = {
|
|
58328
58902
|
".html": "text/html; charset=utf-8",
|
|
58329
58903
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -58378,16 +58952,35 @@ async function serveShare(file, opts = {}) {
|
|
|
58378
58952
|
const q2 = (req.url ?? "").split("?")[1];
|
|
58379
58953
|
return q2 ? new URLSearchParams(q2).get("token") ?? void 0 : void 0;
|
|
58380
58954
|
}
|
|
58381
|
-
const
|
|
58955
|
+
const agentEntries = opts.agents && opts.agents.length > 0 ? opts.agents.map((a2) => ({ name: a2.name, token: randomUUID2(), rateLimit: a2.rateLimit })) : [{ name: "agent", token: randomUUID2() }];
|
|
58956
|
+
const agentToken = agentEntries[0].token;
|
|
58957
|
+
const agentByToken = new Map(agentEntries.map((e) => [e.token, e]));
|
|
58958
|
+
const rateHits = /* @__PURE__ */ new Map();
|
|
58959
|
+
function withinRateLimit(entry) {
|
|
58960
|
+
if (!entry.rateLimit) return true;
|
|
58961
|
+
const { maxRequests, windowMs } = entry.rateLimit;
|
|
58962
|
+
const now = Date.now();
|
|
58963
|
+
const recent = (rateHits.get(entry.token) ?? []).filter((t2) => now - t2 < windowMs);
|
|
58964
|
+
if (recent.length >= maxRequests) {
|
|
58965
|
+
rateHits.set(entry.token, recent);
|
|
58966
|
+
return false;
|
|
58967
|
+
}
|
|
58968
|
+
recent.push(now);
|
|
58969
|
+
rateHits.set(entry.token, recent);
|
|
58970
|
+
return true;
|
|
58971
|
+
}
|
|
58382
58972
|
const { hocuspocus, session, slug } = await configureCollab(file, {
|
|
58383
58973
|
...opts,
|
|
58384
|
-
authenticate: (token) =>
|
|
58974
|
+
authenticate: (token) => {
|
|
58975
|
+
const role = roleForToken(token);
|
|
58976
|
+
return { readOnly: role === "viewer", role };
|
|
58977
|
+
}
|
|
58385
58978
|
});
|
|
58386
58979
|
const agent4 = createAgentApi(hocuspocus, slug);
|
|
58387
58980
|
async function serveStatic(urlPath, res) {
|
|
58388
58981
|
const isDocRoute = urlPath === "/" || /^\/d\/[^/]+\/?$/.test(urlPath);
|
|
58389
58982
|
const rel = isDocRoute ? "index.html" : urlPath.replace(/^\/d\//, "").replace(/^\/+/, "");
|
|
58390
|
-
const target = normalize4(
|
|
58983
|
+
const target = normalize4(join5(distDir, rel));
|
|
58391
58984
|
if (target !== distDir && !target.startsWith(distDir + "/")) {
|
|
58392
58985
|
res.writeHead(403).end("Forbidden");
|
|
58393
58986
|
return;
|
|
@@ -58429,10 +59022,16 @@ async function serveShare(file, opts = {}) {
|
|
|
58429
59022
|
return;
|
|
58430
59023
|
}
|
|
58431
59024
|
if (urlPath.startsWith(`/api/agent/${slug}/`)) {
|
|
58432
|
-
|
|
59025
|
+
const entry = agentByToken.get(tokenFromRequest(req) ?? "");
|
|
59026
|
+
if (!entry) {
|
|
58433
59027
|
sendJson2(res, 403, { error: "invalid or missing agent token" });
|
|
58434
59028
|
return;
|
|
58435
59029
|
}
|
|
59030
|
+
if (!withinRateLimit(entry)) {
|
|
59031
|
+
sendJson2(res, 429, { error: "rate limit exceeded", agent: entry.name });
|
|
59032
|
+
return;
|
|
59033
|
+
}
|
|
59034
|
+
const modelFrom = (b2) => b2.model ?? entry.name;
|
|
58436
59035
|
if (urlPath === `/api/agent/${slug}/state` && req.method === "GET") {
|
|
58437
59036
|
sendJson2(res, 200, await agent4.getState());
|
|
58438
59037
|
return;
|
|
@@ -58443,7 +59042,7 @@ async function serveShare(file, opts = {}) {
|
|
|
58443
59042
|
sendJson2(res, 400, { error: "comment needs { quote, text }" });
|
|
58444
59043
|
return;
|
|
58445
59044
|
}
|
|
58446
|
-
sendJson2(res, 200, await agent4.addComment({ quote: b2.quote, text: b2.text, model: b2
|
|
59045
|
+
sendJson2(res, 200, await agent4.addComment({ quote: b2.quote, text: b2.text, model: modelFrom(b2) }));
|
|
58447
59046
|
return;
|
|
58448
59047
|
}
|
|
58449
59048
|
if (urlPath === `/api/agent/${slug}/suggest` && req.method === "POST") {
|
|
@@ -58457,7 +59056,20 @@ async function serveShare(file, opts = {}) {
|
|
|
58457
59056
|
replace: b2.replace,
|
|
58458
59057
|
insert: b2.insert,
|
|
58459
59058
|
delete: b2.delete,
|
|
58460
|
-
model: b2
|
|
59059
|
+
model: modelFrom(b2)
|
|
59060
|
+
}));
|
|
59061
|
+
return;
|
|
59062
|
+
}
|
|
59063
|
+
if (urlPath === `/api/agent/${slug}/rewrite` && req.method === "POST") {
|
|
59064
|
+
const b2 = await readJsonBody(req);
|
|
59065
|
+
if (typeof b2.markdown !== "string") {
|
|
59066
|
+
sendJson2(res, 400, { error: "rewrite needs { markdown, quote? }" });
|
|
59067
|
+
return;
|
|
59068
|
+
}
|
|
59069
|
+
sendJson2(res, 200, await agent4.rewrite({
|
|
59070
|
+
markdown: b2.markdown,
|
|
59071
|
+
quote: typeof b2.quote === "string" ? b2.quote : void 0,
|
|
59072
|
+
model: modelFrom(b2)
|
|
58461
59073
|
}));
|
|
58462
59074
|
return;
|
|
58463
59075
|
}
|
|
@@ -58489,6 +59101,7 @@ async function serveShare(file, opts = {}) {
|
|
|
58489
59101
|
url: links.editor,
|
|
58490
59102
|
links,
|
|
58491
59103
|
agentToken,
|
|
59104
|
+
agentTokens: opts.agents ? Object.fromEntries(agentEntries.map((e) => [e.name, e.token])) : void 0,
|
|
58492
59105
|
host,
|
|
58493
59106
|
port: boundPort,
|
|
58494
59107
|
slug,
|
|
@@ -58503,11 +59116,15 @@ async function serveShare(file, opts = {}) {
|
|
|
58503
59116
|
}
|
|
58504
59117
|
|
|
58505
59118
|
// packages/mddocs-cli/src/util/resolve-file.ts
|
|
58506
|
-
function fileForId(opts) {
|
|
58507
|
-
if (
|
|
58508
|
-
|
|
59119
|
+
async function fileForId(id3, opts) {
|
|
59120
|
+
if (opts.file) return opts.file;
|
|
59121
|
+
const found2 = await findFileForMark(id3, process.cwd());
|
|
59122
|
+
if (!found2) {
|
|
59123
|
+
throw new Error(
|
|
59124
|
+
`could not find mark ${id3} in any managed .md file under ${process.cwd()} (pass --file <path> to point at it directly)`
|
|
59125
|
+
);
|
|
58509
59126
|
}
|
|
58510
|
-
return
|
|
59127
|
+
return found2;
|
|
58511
59128
|
}
|
|
58512
59129
|
function actor() {
|
|
58513
59130
|
return `human:${process.env.USER ?? "unknown"}`;
|
|
@@ -58544,7 +59161,7 @@ function registerComment(program2) {
|
|
|
58544
59161
|
}
|
|
58545
59162
|
});
|
|
58546
59163
|
cmd.command("reply <id>").requiredOption("--text <t>").option("--file <f>").action(async (id3, o2) => {
|
|
58547
|
-
const file = fileForId(o2);
|
|
59164
|
+
const file = await fileForId(id3, o2);
|
|
58548
59165
|
const doc4 = await loadDoc(file);
|
|
58549
59166
|
const mark = doc4.marks[id3];
|
|
58550
59167
|
if (!mark || mark.kind !== "comment") throw new Error(`no comment with id ${id3} in ${file}`);
|
|
@@ -58557,7 +59174,7 @@ function registerComment(program2) {
|
|
|
58557
59174
|
console.log(`replied to ${id3}`);
|
|
58558
59175
|
});
|
|
58559
59176
|
cmd.command("resolve <id>").option("--file <f>").action(async (id3, o2) => {
|
|
58560
|
-
const file = fileForId(o2);
|
|
59177
|
+
const file = await fileForId(id3, o2);
|
|
58561
59178
|
const doc4 = await loadDoc(file);
|
|
58562
59179
|
const next = proof_exports.resolveComment(toArray(doc4.marks), id3);
|
|
58563
59180
|
await saveDoc(file, doc4.content, toRecord(next));
|
|
@@ -58586,18 +59203,28 @@ function registerSuggest(program2) {
|
|
|
58586
59203
|
}
|
|
58587
59204
|
|
|
58588
59205
|
// packages/mddocs-cli/src/commands/accept-reject.ts
|
|
58589
|
-
|
|
58590
|
-
|
|
58591
|
-
|
|
59206
|
+
var SUGGESTION_KINDS = ["insert", "delete", "replace"];
|
|
59207
|
+
function registerAcceptReject(program2) {
|
|
59208
|
+
program2.command("accept <id>").option("--file <f>").action(async (id3, o2) => {
|
|
59209
|
+
const file = await fileForId(id3, o2);
|
|
58592
59210
|
const doc4 = await loadDoc(file);
|
|
58593
|
-
const
|
|
59211
|
+
const stored = doc4.marks[id3];
|
|
59212
|
+
const mark = stored && { ...stored, id: id3 };
|
|
59213
|
+
if (!mark || !SUGGESTION_KINDS.includes(mark.kind)) {
|
|
59214
|
+
throw new Error(`no suggestion with id ${id3} in ${file}`);
|
|
59215
|
+
}
|
|
59216
|
+
const content3 = applySuggestion(doc4.content, mark);
|
|
59217
|
+
const { [id3]: _applied, ...rest } = doc4.marks;
|
|
59218
|
+
await saveDoc(file, content3, rest);
|
|
59219
|
+
console.log(`accepted ${id3} (applied to ${file})`);
|
|
59220
|
+
});
|
|
59221
|
+
program2.command("reject <id>").option("--file <f>").action(async (id3, o2) => {
|
|
59222
|
+
const file = await fileForId(id3, o2);
|
|
59223
|
+
const doc4 = await loadDoc(file);
|
|
59224
|
+
const next = proof_exports.rejectSuggestion(toArray(doc4.marks), id3);
|
|
58594
59225
|
await saveDoc(file, doc4.content, toRecord(next));
|
|
58595
|
-
console.log(
|
|
58596
|
-
};
|
|
58597
|
-
}
|
|
58598
|
-
function registerAcceptReject(program2) {
|
|
58599
|
-
program2.command("accept <id>").option("--file <f>").action(decide(proof_exports.acceptSuggestion, "accepted"));
|
|
58600
|
-
program2.command("reject <id>").option("--file <f>").action(decide(proof_exports.rejectSuggestion, "rejected"));
|
|
59226
|
+
console.log(`rejected ${id3}`);
|
|
59227
|
+
});
|
|
58601
59228
|
}
|
|
58602
59229
|
|
|
58603
59230
|
// packages/mddocs-cli/src/commands/history.ts
|
|
@@ -58654,21 +59281,34 @@ function registerOpen(program2) {
|
|
|
58654
59281
|
// packages/mddocs-cli/src/commands/serve.ts
|
|
58655
59282
|
import { dirname as dirname5 } from "node:path";
|
|
58656
59283
|
function registerServe(program2) {
|
|
58657
|
-
program2.command("serve <file>").description("host a live multiplayer editing session (share the URL on your LAN)").option("--port <n>", "port to listen on", (v) => parseInt(v, 10)).option("--host <h>", "interface to bind (use 0.0.0.0 to share on your LAN)", "127.0.0.1").option("--no-autocommit", "do not auto-commit edits to git").action(async (file, o2) => {
|
|
59284
|
+
program2.command("serve <file>").description("host a live multiplayer editing session (share the URL on your LAN)").option("--port <n>", "port to listen on", (v) => parseInt(v, 10)).option("--host <h>", "interface to bind (use 0.0.0.0 to share on your LAN)", "127.0.0.1").option("--no-autocommit", "do not auto-commit edits to git").option("--agent <name>", "register a named agent with its own token (repeatable)", (v, acc) => [...acc, v], []).action(async (file, o2) => {
|
|
58658
59285
|
const autocommit = o2.autocommit !== false;
|
|
58659
59286
|
if (autocommit && !await isGitRepo(dirname5(file))) {
|
|
58660
59287
|
console.warn("mddocs: not a git repo - history/autocommit disabled. Run `mddocs init` + `git init` to enable.");
|
|
58661
59288
|
}
|
|
58662
|
-
const
|
|
59289
|
+
const agents = (o2.agent ?? []).map((name2) => ({ name: name2 }));
|
|
59290
|
+
const handle2 = await serveShare(file, {
|
|
59291
|
+
port: o2.port,
|
|
59292
|
+
host: o2.host,
|
|
59293
|
+
autocommit,
|
|
59294
|
+
agents: agents.length > 0 ? agents : void 0
|
|
59295
|
+
});
|
|
58663
59296
|
console.log(`mddocs: live session for ${file}`);
|
|
58664
59297
|
console.log(` edit (you): ${handle2.links.editor}`);
|
|
58665
59298
|
console.log(` comment link: ${handle2.links.commenter}`);
|
|
58666
59299
|
console.log(` view link: ${handle2.links.viewer}`);
|
|
58667
59300
|
console.log(" (share the link matching the access you want to grant)");
|
|
58668
59301
|
console.log("");
|
|
58669
|
-
console.log(" agent API (programmatic comments/suggestions, live + git-backed):");
|
|
59302
|
+
console.log(" agent API (programmatic comments/suggestions/rewrites, live + git-backed):");
|
|
58670
59303
|
console.log(` base: http://${handle2.host}:${handle2.port}/api/agent/${handle2.slug}`);
|
|
58671
|
-
|
|
59304
|
+
if (handle2.agentTokens) {
|
|
59305
|
+
console.log(" tokens (send as header: x-share-token):");
|
|
59306
|
+
for (const [name2, token] of Object.entries(handle2.agentTokens)) {
|
|
59307
|
+
console.log(` ${name2}: ${token}`);
|
|
59308
|
+
}
|
|
59309
|
+
} else {
|
|
59310
|
+
console.log(` token: ${handle2.agentToken} (send as header: x-share-token)`);
|
|
59311
|
+
}
|
|
58672
59312
|
console.log(` e.g. curl -H "x-share-token: ${handle2.agentToken}" -H 'content-type: application/json' \\`);
|
|
58673
59313
|
console.log(` -d '{"quote":"...","text":"..."}' http://${handle2.host}:${handle2.port}/api/agent/${handle2.slug}/comment`);
|
|
58674
59314
|
console.log(" (Ctrl-C to stop)");
|
|
@@ -58696,11 +59336,11 @@ function registerResolve(program2) {
|
|
|
58696
59336
|
|
|
58697
59337
|
// packages/mddocs-cli/src/commands/init.ts
|
|
58698
59338
|
import { readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
|
|
58699
|
-
import { join as
|
|
59339
|
+
import { join as join6 } from "node:path";
|
|
58700
59340
|
var MD_LINE = "*.md diff text";
|
|
58701
59341
|
function registerInit(program2) {
|
|
58702
59342
|
program2.command("init").description("mark this repo as mddocs-managed (.gitattributes)").action(async () => {
|
|
58703
|
-
const attrsPath =
|
|
59343
|
+
const attrsPath = join6(process.cwd(), ".gitattributes");
|
|
58704
59344
|
let existing = "";
|
|
58705
59345
|
try {
|
|
58706
59346
|
existing = await readFile5(attrsPath, "utf8");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devyrpauli/mddocs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Local-first, git-native collaborative Markdown: real-time multiplayer, comments and suggestions, and an HTTP API for AI agents. Self-hostable, built on proof-sdk.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|