@pilotiq/tiptap 3.11.0 → 3.13.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/CHANGELOG.md +20 -0
- package/dist/Block.d.ts +8 -2
- package/dist/Block.js +9 -3
- package/dist/RichTextField.d.ts +17 -2
- package/dist/RichTextField.js +38 -7
- package/dist/blocks/alert.d.ts +10 -0
- package/dist/blocks/alert.js +23 -0
- package/dist/blocks/faq.d.ts +9 -0
- package/dist/blocks/faq.js +21 -0
- package/dist/blocks/index.d.ts +22 -0
- package/dist/blocks/index.js +20 -0
- package/dist/blocks/keyTakeaways.d.ts +10 -0
- package/dist/blocks/keyTakeaways.js +15 -0
- package/dist/blocks/prosCons.d.ts +9 -0
- package/dist/blocks/prosCons.js +15 -0
- package/dist/blocks/summary.d.ts +9 -0
- package/dist/blocks/summary.js +14 -0
- package/dist/extensions/AiInlineDiffExtension.d.ts +14 -2
- package/dist/extensions/AiInlineDiffExtension.js +175 -5
- package/dist/extensions/DragHandleExtension.js +20 -3
- package/dist/extensions/SlashCommandExtension.js +71 -0
- package/dist/extensions/contentBlocks.d.ts +19 -0
- package/dist/extensions/contentBlocks.js +281 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/react/CollabTextRenderer.js +2 -1
- package/dist/react/MarkdownEditor.js +2 -1
- package/dist/react/TiptapEditor.js +6 -1
- package/dist/react/useAiInlineDiff.d.ts +16 -0
- package/dist/react/useAiInlineDiff.js +23 -2
- package/dist/render.d.ts +5 -2
- package/dist/render.js +136 -2
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -26,8 +26,11 @@
|
|
|
26
26
|
* / image.src + alt + title + width + height
|
|
27
27
|
* / tableCell.colspan + rowspan + colwidth (also tableHeader)
|
|
28
28
|
* / mergeTag.id / mention.id + label + trigger
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* default blocks — the `pilotiqBlock` node's built-in types (faq / alert /
|
|
30
|
+
* summary / key-takeaways / pros-cons) render to semantic
|
|
31
|
+
* `<div class="pilotiq-...">` markup; consumers own the CSS.
|
|
32
|
+
* custom blocks — any other type renders to `<div data-type="..."
|
|
33
|
+
* data-attrs="...">` so consumers can replay or style by data-type.
|
|
31
34
|
*/
|
|
32
35
|
/**
|
|
33
36
|
* Render Tiptap content to HTML.
|
|
@@ -139,9 +142,20 @@ function renderNode(node, opts) {
|
|
|
139
142
|
case 'detailsContent': return renderChildren(n, opts);
|
|
140
143
|
case 'grid': return renderGrid(n, opts);
|
|
141
144
|
case 'gridColumn': return wrap('div', n, opts);
|
|
145
|
+
case 'keyTakeaways': return labeledBlockHtml('pilotiq-key-takeaways', 'Key takeaways', n, opts);
|
|
146
|
+
case 'summary': return labeledBlockHtml('pilotiq-summary', 'Summary', n, opts);
|
|
147
|
+
case 'faq': return labeledBlockHtml('pilotiq-faq', 'FAQ', n, opts);
|
|
148
|
+
case 'faqItem': return `<div class="pilotiq-faq-item">${renderChildren(n, opts)}</div>`;
|
|
149
|
+
case 'faqQuestion': return `<div class="pilotiq-faq-question"><span class="pilotiq-faq-marker">Q</span><span class="pilotiq-faq-text">${renderChildren(n, opts)}</span></div>`;
|
|
150
|
+
case 'faqAnswer': return `<div class="pilotiq-faq-answer"><span class="pilotiq-faq-marker">A</span><div class="pilotiq-faq-body">${renderChildren(n, opts)}</div></div>`;
|
|
151
|
+
case 'alert': return renderAlertNode(n, opts);
|
|
152
|
+
case 'prosCons': return `<div class="pilotiq-pros-cons">${renderChildren(n, opts)}</div>`;
|
|
153
|
+
case 'prosColumn': return labeledBlockHtml('pilotiq-pros', 'Pros', n, opts);
|
|
154
|
+
case 'consColumn': return labeledBlockHtml('pilotiq-cons', 'Cons', n, opts);
|
|
142
155
|
case 'mergeTag': return renderMergeTag(n, opts);
|
|
143
156
|
case 'mention': return renderMention(n, opts);
|
|
144
157
|
case 'text': return renderText(n);
|
|
158
|
+
case 'pilotiqBlock': return renderPilotiqBlock(n, opts);
|
|
145
159
|
default:
|
|
146
160
|
if (opts.renderBlock)
|
|
147
161
|
return opts.renderBlock(n);
|
|
@@ -340,6 +354,28 @@ function clampGridColumnsForRender(raw) {
|
|
|
340
354
|
const trunc = Math.trunc(n);
|
|
341
355
|
return trunc === 3 ? 3 : 2;
|
|
342
356
|
}
|
|
357
|
+
// ─── Inline content blocks (labelled editable nodes) ─────────────────
|
|
358
|
+
//
|
|
359
|
+
// Mirrors the editor's `renderHTML` (extensions/contentBlocks.ts): a small
|
|
360
|
+
// label above an editable body. Consumer owns the `pilotiq-*` CSS. Covers
|
|
361
|
+
// keyTakeaways / summary / faq / alert / prosCons (+ pros/cons columns).
|
|
362
|
+
function labeledBlockHtml(cssClass, label, n, opts) {
|
|
363
|
+
return (`<div class="${cssClass}">` +
|
|
364
|
+
`<div class="pilotiq-block-label">${escapeHtml(label)}</div>` +
|
|
365
|
+
`<div class="pilotiq-block-body">${renderChildren(n, opts)}</div>` +
|
|
366
|
+
`</div>`);
|
|
367
|
+
}
|
|
368
|
+
const ALERT_NODE_TYPES = new Set(['info', 'warning', 'success', 'tip']);
|
|
369
|
+
const ALERT_NODE_LABEL = { info: 'Info', warning: 'Warning', success: 'Success', tip: 'Tip' };
|
|
370
|
+
function renderAlertNode(n, opts) {
|
|
371
|
+
let type = String(n.attrs?.['type'] ?? '').trim().toLowerCase();
|
|
372
|
+
if (!ALERT_NODE_TYPES.has(type))
|
|
373
|
+
type = 'info';
|
|
374
|
+
return (`<div class="pilotiq-alert pilotiq-alert-${type}" role="note">` +
|
|
375
|
+
`<div class="pilotiq-block-label">${ALERT_NODE_LABEL[type]}</div>` +
|
|
376
|
+
`<div class="pilotiq-block-body">${renderChildren(n, opts)}</div>` +
|
|
377
|
+
`</div>`);
|
|
378
|
+
}
|
|
343
379
|
// ─── Merge tags + mentions ───────────────────────────────────────────
|
|
344
380
|
/**
|
|
345
381
|
* Render a `mergeTag` atom — either substitute the value from
|
|
@@ -386,6 +422,104 @@ function renderCustomBlock(n) {
|
|
|
386
422
|
const inner = n.content ? renderChildren(n, {}) : '';
|
|
387
423
|
return `<div data-type="${escapeAttr(type)}"${dataAttrs}>${inner}</div>`;
|
|
388
424
|
}
|
|
425
|
+
// ─── Default blocks (pilotiqBlock node, keyed on attrs.blockType) ─────
|
|
426
|
+
//
|
|
427
|
+
// The custom-block node (`pilotiqBlock`) carries `blockType` + `blockData`.
|
|
428
|
+
// The blocks shipped by default in `RichTextField` (FAQ / Alert / Summary /
|
|
429
|
+
// Key takeaways / Pros & cons) render to semantic HTML here; every other
|
|
430
|
+
// (host-defined) block type falls back to `opts.renderBlock` or the generic
|
|
431
|
+
// `data-attrs` div. Consumers own the CSS for the `pilotiq-*` classes.
|
|
432
|
+
function renderPilotiqBlock(n, opts) {
|
|
433
|
+
const attrs = (n.attrs ?? {});
|
|
434
|
+
const blockType = String(attrs['blockType'] ?? '');
|
|
435
|
+
let data = attrs['blockData'];
|
|
436
|
+
if (typeof data === 'string') {
|
|
437
|
+
try {
|
|
438
|
+
data = JSON.parse(data);
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
data = {};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const d = (data && typeof data === 'object' ? data : {});
|
|
445
|
+
switch (blockType) {
|
|
446
|
+
case 'faq': return renderFaqBlock(d);
|
|
447
|
+
case 'alert': return renderAlertBlock(d);
|
|
448
|
+
case 'summary': return renderSummaryBlock(d);
|
|
449
|
+
case 'key-takeaways': return renderKeyTakeawaysBlock(d);
|
|
450
|
+
case 'pros-cons': return renderProsConsBlock(d);
|
|
451
|
+
default:
|
|
452
|
+
if (opts.renderBlock)
|
|
453
|
+
return opts.renderBlock(n);
|
|
454
|
+
return renderCustomBlock(n);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/** Escape a plain-text field value and split blank-line-separated paragraphs. */
|
|
458
|
+
function blockParagraphs(raw) {
|
|
459
|
+
const s = String(raw ?? '').trim();
|
|
460
|
+
if (s === '')
|
|
461
|
+
return '';
|
|
462
|
+
return s
|
|
463
|
+
.split(/\n{2,}/)
|
|
464
|
+
.map((p) => `<p>${escapeHtml(p).replace(/\n/g, '<br>')}</p>`)
|
|
465
|
+
.join('');
|
|
466
|
+
}
|
|
467
|
+
/** Coerce a field value to a clean `string[]` (TagsInput stores an array). */
|
|
468
|
+
function blockStringList(raw) {
|
|
469
|
+
if (!Array.isArray(raw))
|
|
470
|
+
return [];
|
|
471
|
+
return raw.map((x) => String(x ?? '').trim()).filter((s) => s !== '');
|
|
472
|
+
}
|
|
473
|
+
function renderFaqBlock(d) {
|
|
474
|
+
const items = Array.isArray(d['items']) ? d['items'] : [];
|
|
475
|
+
const body = items
|
|
476
|
+
.map((it) => {
|
|
477
|
+
const q = escapeHtml(String(it['question'] ?? '').trim());
|
|
478
|
+
if (q === '')
|
|
479
|
+
return '';
|
|
480
|
+
const a = blockParagraphs(it['answer']);
|
|
481
|
+
return `<details class="pilotiq-faq-item"><summary>${q}</summary><div class="pilotiq-faq-answer">${a}</div></details>`;
|
|
482
|
+
})
|
|
483
|
+
.join('');
|
|
484
|
+
if (body === '')
|
|
485
|
+
return '';
|
|
486
|
+
return `<div class="pilotiq-faq">${body}</div>`;
|
|
487
|
+
}
|
|
488
|
+
const ALERT_TYPES = new Set(['info', 'warning', 'success', 'tip']);
|
|
489
|
+
function renderAlertBlock(d) {
|
|
490
|
+
let type = String(d['type'] ?? '').trim().toLowerCase();
|
|
491
|
+
if (!ALERT_TYPES.has(type))
|
|
492
|
+
type = 'info';
|
|
493
|
+
const content = blockParagraphs(d['content']);
|
|
494
|
+
return `<div class="pilotiq-alert pilotiq-alert-${type}" role="note">${content}</div>`;
|
|
495
|
+
}
|
|
496
|
+
function renderSummaryBlock(d) {
|
|
497
|
+
const content = blockParagraphs(d['content']);
|
|
498
|
+
if (content === '')
|
|
499
|
+
return '';
|
|
500
|
+
return (`<div class="pilotiq-summary">` +
|
|
501
|
+
`<div class="pilotiq-summary-label">Summary</div>` +
|
|
502
|
+
`<div class="pilotiq-summary-body">${content}</div>` +
|
|
503
|
+
`</div>`);
|
|
504
|
+
}
|
|
505
|
+
function renderKeyTakeawaysBlock(d) {
|
|
506
|
+
const points = blockStringList(d['points']);
|
|
507
|
+
if (points.length === 0)
|
|
508
|
+
return '';
|
|
509
|
+
const lis = points.map((p) => `<li>${escapeHtml(p)}</li>`).join('');
|
|
510
|
+
return (`<div class="pilotiq-key-takeaways">` +
|
|
511
|
+
`<div class="pilotiq-key-takeaways-label">Key takeaways</div>` +
|
|
512
|
+
`<ul>${lis}</ul>` +
|
|
513
|
+
`</div>`);
|
|
514
|
+
}
|
|
515
|
+
function renderProsConsBlock(d) {
|
|
516
|
+
const pros = blockStringList(d['pros']);
|
|
517
|
+
const cons = blockStringList(d['cons']);
|
|
518
|
+
if (pros.length === 0 && cons.length === 0)
|
|
519
|
+
return '';
|
|
520
|
+
const column = (label, cls, items) => `<div class="pilotiq-${cls}"><h4>${label}</h4><ul>${items.map((i) => `<li>${escapeHtml(i)}</li>`).join('')}</ul></div>`;
|
|
521
|
+
return `<div class="pilotiq-pros-cons">${column('Pros', 'pros', pros)}${column('Cons', 'cons', cons)}</div>`;
|
|
522
|
+
}
|
|
389
523
|
// ─── Escapers + sanitizers ───────────────────────────────────────────
|
|
390
524
|
function escapeHtml(s) {
|
|
391
525
|
return s.replace(/[&<>"']/g, ch => HTML_ESCAPES[ch] ?? ch);
|