@xnetjs/plugins 0.12.0 → 2.0.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/dist/index.d.ts +126 -47
- package/dist/index.js +541 -14
- package/dist/services/node.d.ts +80 -3
- package/dist/services/node.js +529 -14
- package/package.json +6 -7
package/dist/services/node.js
CHANGED
|
@@ -1234,7 +1234,7 @@ var applyPageMarkdownTool = {
|
|
|
1234
1234
|
definition: {
|
|
1235
1235
|
name: "xnet_apply_page_markdown",
|
|
1236
1236
|
title: "Apply page Markdown plan",
|
|
1237
|
-
description: "Apply a validated page Markdown mutation plan through the configured
|
|
1237
|
+
description: "Apply a validated page Markdown mutation plan through the configured BlockNote/Yjs document adapter, with a node-property fallback.",
|
|
1238
1238
|
risk: "high",
|
|
1239
1239
|
requiredScopes: ["page.read", "page.write"],
|
|
1240
1240
|
inputSchema: {
|
|
@@ -4028,6 +4028,514 @@ function isRecord4(value) {
|
|
|
4028
4028
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4029
4029
|
}
|
|
4030
4030
|
|
|
4031
|
+
// src/ai-surface/page-fragment.ts
|
|
4032
|
+
import * as Y from "yjs";
|
|
4033
|
+
var XNET_PAGE_FRAGMENT_FIELD = "content-v4";
|
|
4034
|
+
var XNET_PAGE_LEGACY_FRAGMENT_FIELD = "content";
|
|
4035
|
+
function isRecord5(value) {
|
|
4036
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4037
|
+
}
|
|
4038
|
+
function attrString(attrs, ...keys) {
|
|
4039
|
+
for (const key of keys) {
|
|
4040
|
+
const value = attrs[key];
|
|
4041
|
+
if (value !== void 0 && value !== null && value !== "") return String(value);
|
|
4042
|
+
}
|
|
4043
|
+
return "";
|
|
4044
|
+
}
|
|
4045
|
+
function markWrap(text, attributes) {
|
|
4046
|
+
if (!attributes || text === "") return text;
|
|
4047
|
+
let out = text;
|
|
4048
|
+
if (attributes.code) out = `\`${out}\``;
|
|
4049
|
+
if (attributes.bold) out = `**${out}**`;
|
|
4050
|
+
if (attributes.italic) out = `*${out}*`;
|
|
4051
|
+
const link = attributes.link;
|
|
4052
|
+
const href = isRecord5(link) ? link.href : link;
|
|
4053
|
+
if (typeof href === "string" && href) out = `[${out}](${href})`;
|
|
4054
|
+
return out;
|
|
4055
|
+
}
|
|
4056
|
+
function textToMarkdown(node) {
|
|
4057
|
+
let out = "";
|
|
4058
|
+
const delta = node.toDelta();
|
|
4059
|
+
for (const op of delta) {
|
|
4060
|
+
if (typeof op.insert === "string") {
|
|
4061
|
+
out += markWrap(op.insert, op.attributes);
|
|
4062
|
+
} else if (isRecord5(op.insert)) {
|
|
4063
|
+
out += embeddedAtomToMarkdown(op.insert);
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
return out;
|
|
4067
|
+
}
|
|
4068
|
+
function embeddedAtomToMarkdown(embed) {
|
|
4069
|
+
const type = typeof embed.type === "string" ? embed.type : "";
|
|
4070
|
+
const attrs = isRecord5(embed.attrs) ? embed.attrs : embed;
|
|
4071
|
+
return atomToMarkdown(type, attrs) ?? "";
|
|
4072
|
+
}
|
|
4073
|
+
function atomToMarkdown(name, attrs) {
|
|
4074
|
+
switch (name) {
|
|
4075
|
+
case "mention":
|
|
4076
|
+
case "personMention":
|
|
4077
|
+
case "taskMention":
|
|
4078
|
+
return `@${attrString(attrs, "label", "id")}`;
|
|
4079
|
+
case "hashtag":
|
|
4080
|
+
return `#${attrString(attrs, "name")}`;
|
|
4081
|
+
case "wikilink":
|
|
4082
|
+
return `[[${attrString(attrs, "title", "href")}]]`;
|
|
4083
|
+
case "inlineMath":
|
|
4084
|
+
return `$${attrString(attrs, "latex")}$`;
|
|
4085
|
+
case "smartReference":
|
|
4086
|
+
case "databaseReference":
|
|
4087
|
+
return attrString(attrs, "title", "url", "databaseId");
|
|
4088
|
+
case "emoji": {
|
|
4089
|
+
const emoji = attrString(attrs, "name");
|
|
4090
|
+
return emoji ? `:${emoji}:` : "";
|
|
4091
|
+
}
|
|
4092
|
+
default:
|
|
4093
|
+
return null;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
function inlineToMarkdown(element) {
|
|
4097
|
+
let out = "";
|
|
4098
|
+
for (let i = 0; i < element.length; i++) {
|
|
4099
|
+
const child = element.get(i);
|
|
4100
|
+
if (child instanceof Y.XmlText) {
|
|
4101
|
+
out += textToMarkdown(child);
|
|
4102
|
+
} else if (child instanceof Y.XmlElement) {
|
|
4103
|
+
const atom = atomToMarkdown(child.nodeName, child.getAttributes());
|
|
4104
|
+
out += atom ?? inlineToMarkdown(child);
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
return out;
|
|
4108
|
+
}
|
|
4109
|
+
var LIST_ITEM_TYPES = /* @__PURE__ */ new Set(["bulletListItem", "numberedListItem", "checkListItem"]);
|
|
4110
|
+
function elementChildren(element) {
|
|
4111
|
+
const children = [];
|
|
4112
|
+
for (let i = 0; i < element.length; i++) {
|
|
4113
|
+
const child = element.get(i);
|
|
4114
|
+
if (child instanceof Y.XmlElement) children.push(child);
|
|
4115
|
+
}
|
|
4116
|
+
return children;
|
|
4117
|
+
}
|
|
4118
|
+
function joinChunks(chunks) {
|
|
4119
|
+
let out = "";
|
|
4120
|
+
chunks.forEach((chunk, index) => {
|
|
4121
|
+
if (index > 0) {
|
|
4122
|
+
const previous = chunks[index - 1];
|
|
4123
|
+
out += previous.kind === "list" && chunk.kind === "list" ? "\n" : "\n\n";
|
|
4124
|
+
}
|
|
4125
|
+
out += chunk.text;
|
|
4126
|
+
});
|
|
4127
|
+
return out;
|
|
4128
|
+
}
|
|
4129
|
+
function indentLines(text, indent) {
|
|
4130
|
+
return text.split("\n").map((line) => line ? indent + line : line).join("\n");
|
|
4131
|
+
}
|
|
4132
|
+
function tableToMarkdown(table) {
|
|
4133
|
+
const rows = [];
|
|
4134
|
+
const visit = (element) => {
|
|
4135
|
+
for (const child of elementChildren(element)) {
|
|
4136
|
+
if (child.nodeName === "tableRow") {
|
|
4137
|
+
const cells = [];
|
|
4138
|
+
for (const cell of elementChildren(child)) {
|
|
4139
|
+
if (cell.nodeName === "tableCell" || cell.nodeName === "tableHeader") {
|
|
4140
|
+
cells.push(inlineToMarkdown(cell).replace(/\n/g, " ").trim());
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
rows.push(cells);
|
|
4144
|
+
} else {
|
|
4145
|
+
visit(child);
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
};
|
|
4149
|
+
visit(table);
|
|
4150
|
+
if (rows.length === 0) return "";
|
|
4151
|
+
const lines = [`| ${rows[0].join(" | ")} |`, `| ${rows[0].map(() => "---").join(" | ")} |`];
|
|
4152
|
+
for (const row of rows.slice(1)) lines.push(`| ${row.join(" | ")} |`);
|
|
4153
|
+
return lines.join("\n");
|
|
4154
|
+
}
|
|
4155
|
+
function blockContentToMarkdown(content, orderedIndex) {
|
|
4156
|
+
const attrs = content.getAttributes();
|
|
4157
|
+
switch (content.nodeName) {
|
|
4158
|
+
case "paragraph":
|
|
4159
|
+
return inlineToMarkdown(content);
|
|
4160
|
+
case "heading": {
|
|
4161
|
+
const level = Math.min(Math.max(Number(attrs.level ?? 1) || 1, 1), 6);
|
|
4162
|
+
return `${"#".repeat(level)} ${inlineToMarkdown(content)}`;
|
|
4163
|
+
}
|
|
4164
|
+
case "bulletListItem":
|
|
4165
|
+
return `- ${inlineToMarkdown(content)}`;
|
|
4166
|
+
case "numberedListItem":
|
|
4167
|
+
return `${orderedIndex}. ${inlineToMarkdown(content)}`;
|
|
4168
|
+
case "checkListItem": {
|
|
4169
|
+
const checked = attrs.checked === true || attrs.checked === "true";
|
|
4170
|
+
return `- [${checked ? "x" : " "}] ${inlineToMarkdown(content)}`;
|
|
4171
|
+
}
|
|
4172
|
+
case "codeBlock": {
|
|
4173
|
+
const language = typeof attrs.language === "string" ? attrs.language : "";
|
|
4174
|
+
return `\`\`\`${language}
|
|
4175
|
+
${inlineToMarkdown(content)}
|
|
4176
|
+
\`\`\``;
|
|
4177
|
+
}
|
|
4178
|
+
case "quote":
|
|
4179
|
+
return inlineToMarkdown(content).split("\n").map((line) => `> ${line}`).join("\n");
|
|
4180
|
+
case "callout": {
|
|
4181
|
+
const kind = attrString(attrs, "kind") || "info";
|
|
4182
|
+
return `> [!${kind}] ${inlineToMarkdown(content)}`;
|
|
4183
|
+
}
|
|
4184
|
+
case "table":
|
|
4185
|
+
return tableToMarkdown(content);
|
|
4186
|
+
case "mermaid":
|
|
4187
|
+
return `\`\`\`mermaid
|
|
4188
|
+
${attrString(attrs, "code") || inlineToMarkdown(content)}
|
|
4189
|
+
\`\`\``;
|
|
4190
|
+
case "embed":
|
|
4191
|
+
case "richLink":
|
|
4192
|
+
return attrString(attrs, "url");
|
|
4193
|
+
case "pageEmbed":
|
|
4194
|
+
return `[[${attrString(attrs, "title", "nodeId")}]]`;
|
|
4195
|
+
case "image":
|
|
4196
|
+
return `})`;
|
|
4197
|
+
case "divider":
|
|
4198
|
+
case "horizontalRule":
|
|
4199
|
+
return "---";
|
|
4200
|
+
default:
|
|
4201
|
+
return inlineToMarkdown(content);
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
function blockGroupToChunks(group) {
|
|
4205
|
+
const chunks = [];
|
|
4206
|
+
let orderedIndex = 0;
|
|
4207
|
+
for (const container of elementChildren(group)) {
|
|
4208
|
+
if (container.nodeName !== "blockContainer") {
|
|
4209
|
+
chunks.push(...blockGroupToChunks(container));
|
|
4210
|
+
continue;
|
|
4211
|
+
}
|
|
4212
|
+
const children = elementChildren(container);
|
|
4213
|
+
const content = children.find((child) => child.nodeName !== "blockGroup") ?? null;
|
|
4214
|
+
const childGroup = children.find((child) => child.nodeName === "blockGroup") ?? null;
|
|
4215
|
+
const type = content?.nodeName ?? "";
|
|
4216
|
+
orderedIndex = type === "numberedListItem" ? orderedIndex + 1 : 0;
|
|
4217
|
+
const kind = LIST_ITEM_TYPES.has(type) ? "list" : "block";
|
|
4218
|
+
let text = content ? blockContentToMarkdown(content, orderedIndex) : "";
|
|
4219
|
+
if (childGroup) {
|
|
4220
|
+
const nested = joinChunks(blockGroupToChunks(childGroup));
|
|
4221
|
+
if (nested) {
|
|
4222
|
+
const separator = kind === "list" ? "\n" : "\n\n";
|
|
4223
|
+
text = text ? `${text}${separator}${indentLines(nested, " ")}` : indentLines(nested, " ");
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4226
|
+
if (text.trim()) chunks.push({ text, kind });
|
|
4227
|
+
}
|
|
4228
|
+
return chunks;
|
|
4229
|
+
}
|
|
4230
|
+
function blockNoteFragmentToMarkdown(fragment) {
|
|
4231
|
+
return joinChunks(blockGroupToChunks(fragment));
|
|
4232
|
+
}
|
|
4233
|
+
function legacyBlockToLines(element, lines) {
|
|
4234
|
+
const attrs = element.getAttributes();
|
|
4235
|
+
switch (element.nodeName) {
|
|
4236
|
+
case "paragraph": {
|
|
4237
|
+
const text = inlineToMarkdown(element);
|
|
4238
|
+
if (text.trim()) lines.push(text);
|
|
4239
|
+
break;
|
|
4240
|
+
}
|
|
4241
|
+
case "heading": {
|
|
4242
|
+
const level = Math.min(Math.max(Number(attrs.level ?? 1) || 1, 1), 6);
|
|
4243
|
+
lines.push(`${"#".repeat(level)} ${inlineToMarkdown(element)}`);
|
|
4244
|
+
break;
|
|
4245
|
+
}
|
|
4246
|
+
case "codeBlock": {
|
|
4247
|
+
const language = typeof attrs.language === "string" ? attrs.language : "";
|
|
4248
|
+
lines.push(`\`\`\`${language}
|
|
4249
|
+
${inlineToMarkdown(element)}
|
|
4250
|
+
\`\`\``);
|
|
4251
|
+
break;
|
|
4252
|
+
}
|
|
4253
|
+
case "blockquote":
|
|
4254
|
+
case "callout": {
|
|
4255
|
+
const inner = [];
|
|
4256
|
+
legacyChildrenToLines(element, inner);
|
|
4257
|
+
lines.push(inner.map((line) => indentLines(line, "> ").replace(/^> $/gm, ">")).join("\n>\n"));
|
|
4258
|
+
break;
|
|
4259
|
+
}
|
|
4260
|
+
case "bulletList":
|
|
4261
|
+
legacyListToLines(element, false, lines);
|
|
4262
|
+
break;
|
|
4263
|
+
case "orderedList":
|
|
4264
|
+
legacyListToLines(element, true, lines);
|
|
4265
|
+
break;
|
|
4266
|
+
case "taskList":
|
|
4267
|
+
legacyTaskListToLines(element, lines);
|
|
4268
|
+
break;
|
|
4269
|
+
case "horizontalRule":
|
|
4270
|
+
lines.push("---");
|
|
4271
|
+
break;
|
|
4272
|
+
case "image":
|
|
4273
|
+
lines.push(`})`);
|
|
4274
|
+
break;
|
|
4275
|
+
case "embed":
|
|
4276
|
+
case "richLink":
|
|
4277
|
+
lines.push(attrString(attrs, "url"));
|
|
4278
|
+
break;
|
|
4279
|
+
case "pageEmbed":
|
|
4280
|
+
lines.push(`[[${attrString(attrs, "title", "nodeId")}]]`);
|
|
4281
|
+
break;
|
|
4282
|
+
case "mermaid":
|
|
4283
|
+
lines.push(`\`\`\`mermaid
|
|
4284
|
+
${attrString(attrs, "code") || inlineToMarkdown(element)}
|
|
4285
|
+
\`\`\``);
|
|
4286
|
+
break;
|
|
4287
|
+
default:
|
|
4288
|
+
if (element.length > 0 && elementChildren(element).length > 0) {
|
|
4289
|
+
legacyChildrenToLines(element, lines);
|
|
4290
|
+
} else {
|
|
4291
|
+
const text = inlineToMarkdown(element);
|
|
4292
|
+
if (text.trim()) lines.push(text);
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
}
|
|
4296
|
+
function legacyListToLines(element, ordered, lines) {
|
|
4297
|
+
const items = [];
|
|
4298
|
+
let index = 1;
|
|
4299
|
+
for (const child of elementChildren(element)) {
|
|
4300
|
+
if (child.nodeName !== "listItem") continue;
|
|
4301
|
+
const inner = [];
|
|
4302
|
+
legacyChildrenToLines(child, inner);
|
|
4303
|
+
const marker = ordered ? `${index}.` : "-";
|
|
4304
|
+
items.push(`${marker} ${indentLines(inner.join("\n"), " ").trimStart()}`);
|
|
4305
|
+
index += 1;
|
|
4306
|
+
}
|
|
4307
|
+
if (items.length > 0) lines.push(items.join("\n"));
|
|
4308
|
+
}
|
|
4309
|
+
function legacyTaskListToLines(element, lines) {
|
|
4310
|
+
const items = [];
|
|
4311
|
+
for (const child of elementChildren(element)) {
|
|
4312
|
+
if (child.nodeName !== "taskItem") continue;
|
|
4313
|
+
const checkedAttr = child.getAttribute("checked");
|
|
4314
|
+
const checked = checkedAttr === true || checkedAttr === "true";
|
|
4315
|
+
const inner = [];
|
|
4316
|
+
legacyChildrenToLines(child, inner);
|
|
4317
|
+
items.push(`- [${checked ? "x" : " "}] ${indentLines(inner.join("\n"), " ").trimStart()}`);
|
|
4318
|
+
}
|
|
4319
|
+
if (items.length > 0) lines.push(items.join("\n"));
|
|
4320
|
+
}
|
|
4321
|
+
function legacyChildrenToLines(element, lines) {
|
|
4322
|
+
for (let i = 0; i < element.length; i++) {
|
|
4323
|
+
const child = element.get(i);
|
|
4324
|
+
if (child instanceof Y.XmlElement) {
|
|
4325
|
+
legacyBlockToLines(child, lines);
|
|
4326
|
+
} else if (child instanceof Y.XmlText) {
|
|
4327
|
+
const text = textToMarkdown(child);
|
|
4328
|
+
if (text.trim()) lines.push(text);
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
function legacyFragmentToMarkdown(fragment) {
|
|
4333
|
+
const lines = [];
|
|
4334
|
+
legacyChildrenToLines(fragment, lines);
|
|
4335
|
+
return lines.join("\n\n");
|
|
4336
|
+
}
|
|
4337
|
+
function xnetPageFragmentToMarkdown(doc, options = {}) {
|
|
4338
|
+
const fragment = doc.getXmlFragment(options.field ?? XNET_PAGE_FRAGMENT_FIELD);
|
|
4339
|
+
if (fragment.length > 0) return blockNoteFragmentToMarkdown(fragment);
|
|
4340
|
+
const legacy = doc.getXmlFragment(options.legacyField ?? XNET_PAGE_LEGACY_FRAGMENT_FIELD);
|
|
4341
|
+
if (legacy.length > 0) return legacyFragmentToMarkdown(legacy);
|
|
4342
|
+
return "";
|
|
4343
|
+
}
|
|
4344
|
+
var HEADING_PATTERN = /^(#{1,6})\s+(.*)$/;
|
|
4345
|
+
var FENCE_PATTERN = /^```([A-Za-z0-9_-]*)\s*$/;
|
|
4346
|
+
var LIST_ITEM_PATTERN = /^(\s*)(?:([-*+])|(\d+)[.)])\s+(.*)$/;
|
|
4347
|
+
var CHECK_PATTERN = /^\[([ xX])\]\s+(.*)$/;
|
|
4348
|
+
var CALLOUT_PATTERN = /^\[!([a-z][a-z0-9-]*)\]\s?(.*)$/i;
|
|
4349
|
+
function parseMarkdownBlocks(markdown) {
|
|
4350
|
+
const lines = markdown.replace(/\r\n/g, "\n").split("\n");
|
|
4351
|
+
const blocks = [];
|
|
4352
|
+
let listStack = [];
|
|
4353
|
+
let index = 0;
|
|
4354
|
+
const pushTop = (block) => {
|
|
4355
|
+
blocks.push(block);
|
|
4356
|
+
listStack = [];
|
|
4357
|
+
};
|
|
4358
|
+
while (index < lines.length) {
|
|
4359
|
+
const line = lines[index];
|
|
4360
|
+
if (!line.trim()) {
|
|
4361
|
+
index += 1;
|
|
4362
|
+
listStack = [];
|
|
4363
|
+
continue;
|
|
4364
|
+
}
|
|
4365
|
+
const fence = FENCE_PATTERN.exec(line.trim());
|
|
4366
|
+
if (fence) {
|
|
4367
|
+
const code = [];
|
|
4368
|
+
index += 1;
|
|
4369
|
+
while (index < lines.length && !/^```\s*$/.test(lines[index].trim())) {
|
|
4370
|
+
code.push(lines[index]);
|
|
4371
|
+
index += 1;
|
|
4372
|
+
}
|
|
4373
|
+
index += 1;
|
|
4374
|
+
pushTop({
|
|
4375
|
+
type: "codeBlock",
|
|
4376
|
+
props: fence[1] ? { language: fence[1] } : {},
|
|
4377
|
+
text: code.join("\n"),
|
|
4378
|
+
children: []
|
|
4379
|
+
});
|
|
4380
|
+
continue;
|
|
4381
|
+
}
|
|
4382
|
+
const heading = HEADING_PATTERN.exec(line);
|
|
4383
|
+
if (heading) {
|
|
4384
|
+
pushTop({
|
|
4385
|
+
type: "heading",
|
|
4386
|
+
props: { level: heading[1].length },
|
|
4387
|
+
text: heading[2],
|
|
4388
|
+
children: []
|
|
4389
|
+
});
|
|
4390
|
+
index += 1;
|
|
4391
|
+
continue;
|
|
4392
|
+
}
|
|
4393
|
+
const listItem = LIST_ITEM_PATTERN.exec(line);
|
|
4394
|
+
if (listItem) {
|
|
4395
|
+
const indent = Math.floor(listItem[1].replace(/\t/g, " ").length / 2);
|
|
4396
|
+
const rest = listItem[4];
|
|
4397
|
+
const check = listItem[2] ? CHECK_PATTERN.exec(rest) : null;
|
|
4398
|
+
const block = check ? {
|
|
4399
|
+
type: "checkListItem",
|
|
4400
|
+
props: { checked: check[1] !== " " },
|
|
4401
|
+
text: check[2],
|
|
4402
|
+
children: []
|
|
4403
|
+
} : {
|
|
4404
|
+
type: listItem[3] ? "numberedListItem" : "bulletListItem",
|
|
4405
|
+
props: {},
|
|
4406
|
+
text: rest,
|
|
4407
|
+
children: []
|
|
4408
|
+
};
|
|
4409
|
+
while (listStack.length > 0 && listStack[listStack.length - 1].indent >= indent) {
|
|
4410
|
+
listStack.pop();
|
|
4411
|
+
}
|
|
4412
|
+
const parent = listStack[listStack.length - 1];
|
|
4413
|
+
if (parent) {
|
|
4414
|
+
parent.block.children.push(block);
|
|
4415
|
+
} else {
|
|
4416
|
+
blocks.push(block);
|
|
4417
|
+
}
|
|
4418
|
+
listStack.push({ indent, block });
|
|
4419
|
+
index += 1;
|
|
4420
|
+
continue;
|
|
4421
|
+
}
|
|
4422
|
+
if (line.startsWith(">")) {
|
|
4423
|
+
const quoted = [];
|
|
4424
|
+
while (index < lines.length && lines[index].startsWith(">")) {
|
|
4425
|
+
quoted.push(lines[index].replace(/^>\s?/, ""));
|
|
4426
|
+
index += 1;
|
|
4427
|
+
}
|
|
4428
|
+
const callout = CALLOUT_PATTERN.exec(quoted[0] ?? "");
|
|
4429
|
+
if (callout) {
|
|
4430
|
+
pushTop({
|
|
4431
|
+
type: "callout",
|
|
4432
|
+
props: { kind: callout[1].toLowerCase() },
|
|
4433
|
+
text: [callout[2], ...quoted.slice(1)].filter((part) => part !== "").join("\n"),
|
|
4434
|
+
children: []
|
|
4435
|
+
});
|
|
4436
|
+
} else {
|
|
4437
|
+
pushTop({ type: "quote", props: {}, text: quoted.join("\n"), children: [] });
|
|
4438
|
+
}
|
|
4439
|
+
continue;
|
|
4440
|
+
}
|
|
4441
|
+
const paragraph = [line];
|
|
4442
|
+
index += 1;
|
|
4443
|
+
while (index < lines.length) {
|
|
4444
|
+
const next = lines[index];
|
|
4445
|
+
if (!next.trim() || HEADING_PATTERN.test(next) || FENCE_PATTERN.test(next.trim()) || LIST_ITEM_PATTERN.test(next) || next.startsWith(">")) {
|
|
4446
|
+
break;
|
|
4447
|
+
}
|
|
4448
|
+
paragraph.push(next);
|
|
4449
|
+
index += 1;
|
|
4450
|
+
}
|
|
4451
|
+
pushTop({ type: "paragraph", props: {}, text: paragraph.join("\n"), children: [] });
|
|
4452
|
+
}
|
|
4453
|
+
return blocks;
|
|
4454
|
+
}
|
|
4455
|
+
var blockIdCounter = 0;
|
|
4456
|
+
function defaultBlockId() {
|
|
4457
|
+
const cryptoApi = globalThis.crypto;
|
|
4458
|
+
if (cryptoApi?.randomUUID) return cryptoApi.randomUUID();
|
|
4459
|
+
blockIdCounter += 1;
|
|
4460
|
+
return `ai-block-${Date.now().toString(36)}-${blockIdCounter}`;
|
|
4461
|
+
}
|
|
4462
|
+
function setAttr(element, name, value) {
|
|
4463
|
+
element.setAttribute(name, value);
|
|
4464
|
+
}
|
|
4465
|
+
var WIKILINK_SPLIT = /(\[\[[^\]\n]+\]\])/;
|
|
4466
|
+
function inlineNodesForText(text) {
|
|
4467
|
+
const nodes = [];
|
|
4468
|
+
for (const part of text.split(WIKILINK_SPLIT)) {
|
|
4469
|
+
if (!part) continue;
|
|
4470
|
+
const wikilink = /^\[\[([^\]\n]+)\]\]$/.exec(part);
|
|
4471
|
+
if (wikilink) {
|
|
4472
|
+
const atom = new Y.XmlElement("wikilink");
|
|
4473
|
+
setAttr(atom, "title", wikilink[1]);
|
|
4474
|
+
setAttr(atom, "href", "");
|
|
4475
|
+
nodes.push(atom);
|
|
4476
|
+
} else {
|
|
4477
|
+
nodes.push(new Y.XmlText(part));
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4480
|
+
return nodes;
|
|
4481
|
+
}
|
|
4482
|
+
function blockToYElement(block, generateBlockId) {
|
|
4483
|
+
const container = new Y.XmlElement("blockContainer");
|
|
4484
|
+
setAttr(container, "id", generateBlockId());
|
|
4485
|
+
const content = new Y.XmlElement(block.type);
|
|
4486
|
+
for (const [key, value] of Object.entries(block.props)) setAttr(content, key, value);
|
|
4487
|
+
if (block.type === "codeBlock") {
|
|
4488
|
+
if (block.text) content.insert(0, [new Y.XmlText(block.text)]);
|
|
4489
|
+
} else if (block.text) {
|
|
4490
|
+
content.insert(0, inlineNodesForText(block.text));
|
|
4491
|
+
}
|
|
4492
|
+
container.insert(0, [content]);
|
|
4493
|
+
if (block.children.length > 0) {
|
|
4494
|
+
const group = new Y.XmlElement("blockGroup");
|
|
4495
|
+
group.insert(
|
|
4496
|
+
0,
|
|
4497
|
+
block.children.map((child) => blockToYElement(child, generateBlockId))
|
|
4498
|
+
);
|
|
4499
|
+
container.insert(1, [group]);
|
|
4500
|
+
}
|
|
4501
|
+
return container;
|
|
4502
|
+
}
|
|
4503
|
+
function replaceXNetPageFragmentWithMarkdown(doc, markdown, options = {}) {
|
|
4504
|
+
const fragment = doc.getXmlFragment(options.field ?? XNET_PAGE_FRAGMENT_FIELD);
|
|
4505
|
+
const generateBlockId = options.generateBlockId ?? defaultBlockId;
|
|
4506
|
+
const blocks = parseMarkdownBlocks(markdown);
|
|
4507
|
+
doc.transact(() => {
|
|
4508
|
+
if (fragment.length > 0) fragment.delete(0, fragment.length);
|
|
4509
|
+
if (blocks.length === 0) return;
|
|
4510
|
+
const group = new Y.XmlElement("blockGroup");
|
|
4511
|
+
fragment.insert(0, [group]);
|
|
4512
|
+
group.insert(
|
|
4513
|
+
0,
|
|
4514
|
+
blocks.map((block) => blockToYElement(block, generateBlockId))
|
|
4515
|
+
);
|
|
4516
|
+
});
|
|
4517
|
+
}
|
|
4518
|
+
function createBlockNotePageMarkdownAdapter(options) {
|
|
4519
|
+
const field = options.field ?? XNET_PAGE_FRAGMENT_FIELD;
|
|
4520
|
+
return {
|
|
4521
|
+
applyMarkdown: async ({ pageId, bodyMarkdown }) => {
|
|
4522
|
+
const doc = await options.resolveDoc(pageId);
|
|
4523
|
+
if (!doc) {
|
|
4524
|
+
throw new Error(`No document available for page ${pageId}`);
|
|
4525
|
+
}
|
|
4526
|
+
replaceXNetPageFragmentWithMarkdown(doc, bodyMarkdown, {
|
|
4527
|
+
field,
|
|
4528
|
+
...options.generateBlockId ? { generateBlockId: options.generateBlockId } : {}
|
|
4529
|
+
});
|
|
4530
|
+
return { mode: "blocknote-yjs", yjsField: field };
|
|
4531
|
+
},
|
|
4532
|
+
readMarkdown: async (pageId) => {
|
|
4533
|
+
const doc = await options.resolveDoc(pageId);
|
|
4534
|
+
return doc ? xnetPageFragmentToMarkdown(doc, { field }) : null;
|
|
4535
|
+
}
|
|
4536
|
+
};
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4031
4539
|
// src/services/local-api.ts
|
|
4032
4540
|
function constantTimeCompare(a, b) {
|
|
4033
4541
|
if (a.length !== b.length) return false;
|
|
@@ -5549,7 +6057,7 @@ var AiWorkspaceExporter = class {
|
|
|
5549
6057
|
const results = Array.isArray(search.results) ? search.results : [];
|
|
5550
6058
|
const nodes = await Promise.all(
|
|
5551
6059
|
results.map(
|
|
5552
|
-
(result) =>
|
|
6060
|
+
(result) => isRecord6(result) && typeof result.id === "string" ? this.config.store.get(result.id) : Promise.resolve(null)
|
|
5553
6061
|
)
|
|
5554
6062
|
);
|
|
5555
6063
|
return nodes.filter((node) => node !== null);
|
|
@@ -5613,7 +6121,7 @@ This folder is a managed xNet AI workspace projection. Edit files here as propos
|
|
|
5613
6121
|
`xnet://database/${encodeURIComponent(node.id)}/views`
|
|
5614
6122
|
);
|
|
5615
6123
|
const query = await this.aiSurface.callTool("xnet_database_query", { databaseId: node.id });
|
|
5616
|
-
const rows =
|
|
6124
|
+
const rows = isRecord6(query) && Array.isArray(query.rows) ? query.rows : [];
|
|
5617
6125
|
const rowsJsonl = rows.map((row) => JSON.stringify(row)).join("\n");
|
|
5618
6126
|
const rowsContent = rowsJsonl ? `${rowsJsonl}
|
|
5619
6127
|
` : "";
|
|
@@ -5631,7 +6139,7 @@ This folder is a managed xNet AI workspace projection. Edit files here as propos
|
|
|
5631
6139
|
const tsvMinRows = this.config.tsvSidecarMinRows ?? 50;
|
|
5632
6140
|
if (rows.length >= tsvMinRows) {
|
|
5633
6141
|
const tsvPath = `Databases/${stem}.tsv`;
|
|
5634
|
-
const tsvContent = toTsv(rows.filter(
|
|
6142
|
+
const tsvContent = toTsv(rows.filter(isRecord6));
|
|
5635
6143
|
await this.writeText(rootDir, tsvPath, tsvContent, files);
|
|
5636
6144
|
entries.push(manifestEntry(tsvPath, "database", node, tsvContent, exportedAt));
|
|
5637
6145
|
}
|
|
@@ -5641,12 +6149,12 @@ This folder is a managed xNet AI workspace projection. Edit files here as propos
|
|
|
5641
6149
|
const viewport = await this.aiSurface.callTool("xnet_canvas_read_viewport", {
|
|
5642
6150
|
canvasId: node.id
|
|
5643
6151
|
});
|
|
5644
|
-
const objects =
|
|
5645
|
-
const edges =
|
|
6152
|
+
const objects = isRecord6(viewport) && Array.isArray(viewport.objects) ? viewport.objects : [];
|
|
6153
|
+
const edges = isRecord6(viewport) && Array.isArray(viewport.edges) ? viewport.edges : [];
|
|
5646
6154
|
const jsonCanvas = JSON.stringify(
|
|
5647
6155
|
{
|
|
5648
|
-
nodes: objects.filter(
|
|
5649
|
-
edges: edges.filter(
|
|
6156
|
+
nodes: objects.filter(isRecord6).map(toJsonCanvasNode2),
|
|
6157
|
+
edges: edges.filter(isRecord6).map(toJsonCanvasEdge2)
|
|
5650
6158
|
},
|
|
5651
6159
|
null,
|
|
5652
6160
|
2
|
|
@@ -6292,7 +6800,7 @@ function databaseProjectionOperation(path, content, contentHash) {
|
|
|
6292
6800
|
function parseJsonObjectFile(content, path) {
|
|
6293
6801
|
try {
|
|
6294
6802
|
const parsed = JSON.parse(content);
|
|
6295
|
-
if (
|
|
6803
|
+
if (isRecord6(parsed)) return parsed;
|
|
6296
6804
|
throw new Error("JSON root must be an object");
|
|
6297
6805
|
} catch (err) {
|
|
6298
6806
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -6304,7 +6812,7 @@ function parseJsonlObjects(content, path) {
|
|
|
6304
6812
|
return lines.map((line, index) => {
|
|
6305
6813
|
try {
|
|
6306
6814
|
const parsed = JSON.parse(line);
|
|
6307
|
-
if (
|
|
6815
|
+
if (isRecord6(parsed)) return parsed;
|
|
6308
6816
|
throw new Error("line must be a JSON object");
|
|
6309
6817
|
} catch (err) {
|
|
6310
6818
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -6313,10 +6821,10 @@ function parseJsonlObjects(content, path) {
|
|
|
6313
6821
|
});
|
|
6314
6822
|
}
|
|
6315
6823
|
function isManifestEntry(value) {
|
|
6316
|
-
return
|
|
6824
|
+
return isRecord6(value) && typeof value.path === "string" && typeof value.kind === "string" && typeof value.id === "string" && typeof value.schemaId === "string" && typeof value.revision === "string" && typeof value.sha256 === "string" && typeof value.exportedAt === "string";
|
|
6317
6825
|
}
|
|
6318
6826
|
function isMutationPlan2(value) {
|
|
6319
|
-
return
|
|
6827
|
+
return isRecord6(value) && typeof value.id === "string" && typeof value.actor === "string" && typeof value.intent === "string" && Array.isArray(value.changes) && isRecord6(value.validation);
|
|
6320
6828
|
}
|
|
6321
6829
|
function errorKindForChangedFile(err) {
|
|
6322
6830
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -6377,7 +6885,7 @@ function readNumber(record, key) {
|
|
|
6377
6885
|
const value = record[key];
|
|
6378
6886
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
6379
6887
|
}
|
|
6380
|
-
function
|
|
6888
|
+
function isRecord6(value) {
|
|
6381
6889
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6382
6890
|
}
|
|
6383
6891
|
|
|
@@ -7705,7 +8213,10 @@ export {
|
|
|
7705
8213
|
ScriptSandbox,
|
|
7706
8214
|
XNET_AGENT_SKILL_MD,
|
|
7707
8215
|
XNET_MARKDOWN_DIRECTIVE_SPECS,
|
|
8216
|
+
XNET_PAGE_FRAGMENT_FIELD,
|
|
8217
|
+
XNET_PAGE_LEGACY_FRAGMENT_FIELD,
|
|
7708
8218
|
attachAiPlanValidation,
|
|
8219
|
+
blockNoteFragmentToMarkdown,
|
|
7709
8220
|
createAgentScriptContext,
|
|
7710
8221
|
createAiChangeSet,
|
|
7711
8222
|
createAiOperation,
|
|
@@ -7713,6 +8224,7 @@ export {
|
|
|
7713
8224
|
createAiValidationResult,
|
|
7714
8225
|
createAiWorkspaceExporter,
|
|
7715
8226
|
createAiWorkspaceWatcher,
|
|
8227
|
+
createBlockNotePageMarkdownAdapter,
|
|
7716
8228
|
createLocalAPI,
|
|
7717
8229
|
createMCPServer,
|
|
7718
8230
|
createMcpHttpServer,
|
|
@@ -7724,13 +8236,16 @@ export {
|
|
|
7724
8236
|
isAiRiskLevel,
|
|
7725
8237
|
isAiScope,
|
|
7726
8238
|
isAiTargetKind,
|
|
8239
|
+
legacyFragmentToMarkdown,
|
|
7727
8240
|
parseAiMutationPlan,
|
|
7728
8241
|
parseXNetPageFrontmatter,
|
|
7729
8242
|
renderMarkdownLineDiff,
|
|
7730
8243
|
renderMarkdownReviewDiff,
|
|
8244
|
+
replaceXNetPageFragmentWithMarkdown,
|
|
7731
8245
|
serializeAiMutationPlan,
|
|
7732
8246
|
stripXNetPageFrontmatter,
|
|
7733
8247
|
toTsv,
|
|
7734
8248
|
validateAiMutationPlan,
|
|
7735
|
-
validateXNetPageMarkdown
|
|
8249
|
+
validateXNetPageMarkdown,
|
|
8250
|
+
xnetPageFragmentToMarkdown
|
|
7736
8251
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnetjs/plugins",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,25 +30,24 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"acorn": "^8.15.0",
|
|
33
|
-
"
|
|
34
|
-
"@xnetjs/
|
|
33
|
+
"yjs": "^13.6.24",
|
|
34
|
+
"@xnetjs/abuse": "2.0.0",
|
|
35
|
+
"@xnetjs/core": "2.0.0",
|
|
35
36
|
"@xnetjs/trust": "0.0.2",
|
|
36
|
-
"@xnetjs/data": "0.
|
|
37
|
+
"@xnetjs/data": "2.0.0",
|
|
37
38
|
"@xnetjs/slack-compat": "0.0.2"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
|
-
"@tiptap/core": "^2.0.0",
|
|
41
41
|
"react": "^18.0.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@tiptap/core": "^2.0.0",
|
|
45
44
|
"@types/react": "^18.2.0",
|
|
46
45
|
"jsdom": "^26.0.0",
|
|
47
46
|
"react": "^18.3.1",
|
|
48
47
|
"tsup": "^8.0.0",
|
|
49
48
|
"typescript": "^5.4.0",
|
|
50
49
|
"vitest": "^4.0.0",
|
|
51
|
-
"@xnetjs/licenses": "0.0.
|
|
50
|
+
"@xnetjs/licenses": "0.0.19"
|
|
52
51
|
},
|
|
53
52
|
"scripts": {
|
|
54
53
|
"build": "tsup",
|