zentao-api 0.6.4 → 0.6.6
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/browser/zentao-api.global.js +26 -18
- package/dist/client/index.d.ts +4 -1
- package/dist/client/index.js +194 -17
- package/dist/index.d.ts +1 -1
- package/dist/misc/environment.d.ts +0 -2
- package/dist/misc/environment.js +43 -19
- package/dist/misc/errors.d.ts +1 -0
- package/dist/misc/errors.js +1 -0
- package/dist/modules/generated.d.ts +2045 -59
- package/dist/modules/generated.js +1027 -55
- package/dist/modules/override.js +36 -66
- package/dist/modules/query.d.ts +0 -2
- package/dist/modules/query.js +5 -15
- package/dist/modules/registry.d.ts +0 -9669
- package/dist/modules/registry.js +0 -4
- package/dist/modules/resolve.js +1 -1
- package/dist/profiles/index.js +1 -4
- package/dist/request/index.d.ts +12 -4
- package/dist/request/index.js +2 -2
- package/dist/types/client.d.ts +3 -3
- package/dist/types/module.d.ts +6 -2
- package/dist/types/options.d.ts +1 -0
- package/dist/utils/doc-helper/index.d.ts +1 -2
- package/dist/utils/doc-helper/index.js +0 -1
- package/dist/utils/doc-helper/inline.d.ts +0 -1
- package/dist/utils/doc-helper/inline.js +3 -81
- package/dist/utils/doc-helper/shared.d.ts +0 -5
- package/dist/utils/doc-helper/shared.js +0 -34
- package/dist/utils/doc-helper/types.d.ts +1 -13
- package/dist/version.js +2 -2
- package/package.json +9 -8
- package/dist/utils/doc-helper/html.d.ts +0 -3
- package/dist/utils/doc-helper/html.js +0 -442
|
@@ -1,442 +0,0 @@
|
|
|
1
|
-
import { deltaToHtml } from './inline.js';
|
|
2
|
-
import { blockChildren, escapeHtml, escapeHtmlAttribute, formatFileSize, isEdgelessOnly, isRecord, normalizeSnapshot, pageTitleText, plainText, resolveAssetUrl, resolveDocumentUrl, resolveZuiImageUrl, safeLanguage, sanitizeUrl, toFiniteNumber, toRecord, toStringValue, validateSnapshotTree, } from './shared.js';
|
|
3
|
-
const TRANSPARENT_FLAVOURS = new Set([
|
|
4
|
-
'affine:surface',
|
|
5
|
-
'affine:zui-layout-cell',
|
|
6
|
-
]);
|
|
7
|
-
const EMBED_LINK_FLAVOURS = new Set([
|
|
8
|
-
'affine:bookmark',
|
|
9
|
-
'affine:embed-figma',
|
|
10
|
-
'affine:embed-github',
|
|
11
|
-
'affine:embed-loom',
|
|
12
|
-
'affine:embed-youtube',
|
|
13
|
-
]);
|
|
14
|
-
function joinHtml(parts) {
|
|
15
|
-
return parts.filter(Boolean).join('\n');
|
|
16
|
-
}
|
|
17
|
-
function attribute(name, value) {
|
|
18
|
-
return value === undefined || value === null || value === ''
|
|
19
|
-
? ''
|
|
20
|
-
: ` ${name}="${escapeHtmlAttribute(value)}"`;
|
|
21
|
-
}
|
|
22
|
-
function dataAttribute(name, value) {
|
|
23
|
-
return isRecord(value)
|
|
24
|
-
? attribute(name, JSON.stringify(value))
|
|
25
|
-
: attribute(name, value);
|
|
26
|
-
}
|
|
27
|
-
function positiveInteger(value) {
|
|
28
|
-
const number = toFiniteNumber(value);
|
|
29
|
-
return number !== undefined && number > 0
|
|
30
|
-
? Math.min(10000, Math.round(number))
|
|
31
|
-
: undefined;
|
|
32
|
-
}
|
|
33
|
-
function safeDimension(value) {
|
|
34
|
-
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
35
|
-
return String(value);
|
|
36
|
-
}
|
|
37
|
-
if (typeof value !== 'string')
|
|
38
|
-
return '';
|
|
39
|
-
const dimension = value.trim();
|
|
40
|
-
return /^(?:\d+(?:\.\d+)?(?:px|%|em|rem|vh|vw)?|auto)$/i.test(dimension)
|
|
41
|
-
? dimension
|
|
42
|
-
: '';
|
|
43
|
-
}
|
|
44
|
-
function safeAlign(value) {
|
|
45
|
-
return ['left', 'center', 'right'].includes(String(value))
|
|
46
|
-
? String(value)
|
|
47
|
-
: '';
|
|
48
|
-
}
|
|
49
|
-
function renderListGroup(items, state) {
|
|
50
|
-
const type = toStringValue(toRecord(items[0]?.props).type, 'bulleted');
|
|
51
|
-
const tag = type === 'numbered' ? 'ol' : 'ul';
|
|
52
|
-
const className = `${escapeHtmlAttribute(type)}-list`;
|
|
53
|
-
const listItems = items.map(item => {
|
|
54
|
-
const props = toRecord(item.props);
|
|
55
|
-
const checkbox = type === 'todo'
|
|
56
|
-
? `<input type="checkbox" disabled${props.checked ? ' checked' : ''}> `
|
|
57
|
-
: '';
|
|
58
|
-
return `<li class="affine-list-block-container">${checkbox}${deltaToHtml(props.text, state.options, item)}${renderBlocks(blockChildren(item), state)}</li>`;
|
|
59
|
-
});
|
|
60
|
-
return `<${tag} class="${className}">${listItems.join('')}</${tag}>`;
|
|
61
|
-
}
|
|
62
|
-
function optionLabel(column, value) {
|
|
63
|
-
const data = toRecord(column.data);
|
|
64
|
-
const options = Array.isArray(data.options)
|
|
65
|
-
? data.options.filter(isRecord)
|
|
66
|
-
: [];
|
|
67
|
-
return toStringValue(options.find(option => option.id === value)?.value);
|
|
68
|
-
}
|
|
69
|
-
function databaseCell(row, column, cells, state) {
|
|
70
|
-
const columnId = toStringValue(column.id);
|
|
71
|
-
const rowCells = toRecord(cells[toStringValue(row.id)]);
|
|
72
|
-
const cell = toRecord(rowCells[columnId]);
|
|
73
|
-
const value = cell.value;
|
|
74
|
-
switch (column.type) {
|
|
75
|
-
case 'title':
|
|
76
|
-
return deltaToHtml(toRecord(row.props).text, state.options, row);
|
|
77
|
-
case 'rich-text':
|
|
78
|
-
return deltaToHtml(value, state.options, row);
|
|
79
|
-
case 'date': {
|
|
80
|
-
const date = new Date(typeof value === 'number' ? value : Number.NaN);
|
|
81
|
-
if (Number.isNaN(date.getTime()))
|
|
82
|
-
return '';
|
|
83
|
-
const year = date.getFullYear();
|
|
84
|
-
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
85
|
-
const day = String(date.getDate()).padStart(2, '0');
|
|
86
|
-
return `${year}-${month}-${day}`;
|
|
87
|
-
}
|
|
88
|
-
case 'select':
|
|
89
|
-
return escapeHtml(optionLabel(column, value));
|
|
90
|
-
case 'multi-select':
|
|
91
|
-
return escapeHtml(Array.isArray(value)
|
|
92
|
-
? value.map(item => optionLabel(column, item)).filter(Boolean).join(',')
|
|
93
|
-
: '');
|
|
94
|
-
case 'checkbox':
|
|
95
|
-
return value === undefined ? '' : escapeHtml(String(value));
|
|
96
|
-
default:
|
|
97
|
-
return value === undefined || value === null
|
|
98
|
-
? ''
|
|
99
|
-
: escapeHtml(String(value));
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function renderDatabase(block, state) {
|
|
103
|
-
const props = toRecord(block.props);
|
|
104
|
-
const columns = Array.isArray(props.columns)
|
|
105
|
-
? props.columns.filter(isRecord)
|
|
106
|
-
: [];
|
|
107
|
-
const cells = toRecord(props.cells);
|
|
108
|
-
const header = `<tr>${columns
|
|
109
|
-
.map(column => `<th>${escapeHtml(toStringValue(column.name))}</th>`)
|
|
110
|
-
.join('')}</tr>`;
|
|
111
|
-
const body = blockChildren(block)
|
|
112
|
-
.map(row => `<tr>${columns
|
|
113
|
-
.map(column => `<td>${databaseCell(row, column, cells, state)}</td>`)
|
|
114
|
-
.join('')}</tr>`)
|
|
115
|
-
.join('');
|
|
116
|
-
const title = deltaToHtml(props.title, state.options, block);
|
|
117
|
-
return joinHtml([
|
|
118
|
-
title ? `<h5>${title}</h5>` : '',
|
|
119
|
-
`<table class="affine-database-block-container"><thead>${header}</thead><tbody>${body}</tbody></table>`,
|
|
120
|
-
]);
|
|
121
|
-
}
|
|
122
|
-
function renderZuiTable(block, state) {
|
|
123
|
-
const props = toRecord(block.props);
|
|
124
|
-
const rows = Array.isArray(props.rows) ? props.rows.filter(isRecord) : [];
|
|
125
|
-
const firstRowHead = props.firstRowHead !== false;
|
|
126
|
-
const body = rows
|
|
127
|
-
.map((row, rowIndex) => {
|
|
128
|
-
const cells = Array.isArray(row.cells) ? row.cells.filter(isRecord) : [];
|
|
129
|
-
return `<tr>${cells
|
|
130
|
-
.map(cell => {
|
|
131
|
-
const tag = cell.head ?? (rowIndex === 0 && firstRowHead) ? 'th' : 'td';
|
|
132
|
-
const colspan = positiveInteger(cell.colspan);
|
|
133
|
-
const rowspan = positiveInteger(cell.rowspan);
|
|
134
|
-
const width = safeDimension(cell.width);
|
|
135
|
-
const align = safeAlign(cell.align);
|
|
136
|
-
const style = align ? `text-align:${align}` : '';
|
|
137
|
-
return `<${tag}${attribute('colspan', colspan)}${attribute('rowspan', rowspan)}${attribute('width', width)}${attribute('style', style)}>${deltaToHtml(cell.text, state.options, block)}</${tag}>`;
|
|
138
|
-
})
|
|
139
|
-
.join('')}</tr>`;
|
|
140
|
-
})
|
|
141
|
-
.join('');
|
|
142
|
-
return `<table class="affine-table-block-container"${attribute('data-block-id', block.id)}><tbody>${body}</tbody></table>`;
|
|
143
|
-
}
|
|
144
|
-
function renderCustomExportNodes(nodes, state) {
|
|
145
|
-
return nodes
|
|
146
|
-
.filter(isRecord)
|
|
147
|
-
.map(node => {
|
|
148
|
-
const props = toRecord(node.props);
|
|
149
|
-
if (node.type === 'heading') {
|
|
150
|
-
const depth = Math.min(6, Math.max(1, typeof props.depth === 'number' ? props.depth : 2));
|
|
151
|
-
return `<h${depth}>${escapeHtml(toStringValue(props.text))}</h${depth}>`;
|
|
152
|
-
}
|
|
153
|
-
if (node.type === 'table') {
|
|
154
|
-
const columns = Array.isArray(props.cols)
|
|
155
|
-
? props.cols.filter(isRecord)
|
|
156
|
-
: [];
|
|
157
|
-
const data = Array.isArray(props.data) ? props.data.filter(isRecord) : [];
|
|
158
|
-
const header = `<tr>${columns
|
|
159
|
-
.map(column => `<th>${escapeHtml(toStringValue(column.text) || toStringValue(column.name))}</th>`)
|
|
160
|
-
.join('')}</tr>`;
|
|
161
|
-
const body = data
|
|
162
|
-
.map(row => `<tr>${columns
|
|
163
|
-
.map(column => {
|
|
164
|
-
const cell = toRecord(row[toStringValue(column.name)]);
|
|
165
|
-
return `<td>${escapeHtml(toStringValue(cell.text))}</td>`;
|
|
166
|
-
})
|
|
167
|
-
.join('')}</tr>`)
|
|
168
|
-
.join('');
|
|
169
|
-
return `<table class="affine-zui-custom-block-table"><thead>${header}</thead><tbody>${body}</tbody></table>`;
|
|
170
|
-
}
|
|
171
|
-
if (node.type === 'link') {
|
|
172
|
-
const url = sanitizeUrl(props.href);
|
|
173
|
-
const label = toStringValue(props.text) || url;
|
|
174
|
-
return url
|
|
175
|
-
? `<a href="${escapeHtmlAttribute(url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`
|
|
176
|
-
: escapeHtml(label);
|
|
177
|
-
}
|
|
178
|
-
return `<div>${escapeHtml(toStringValue(props.text))}</div>`;
|
|
179
|
-
})
|
|
180
|
-
.join('');
|
|
181
|
-
}
|
|
182
|
-
function renderCustomBlock(block, state) {
|
|
183
|
-
const contentValue = toRecord(block.props).content;
|
|
184
|
-
if (typeof contentValue === 'string') {
|
|
185
|
-
return `<div class="affine-zui-custom-block">${escapeHtml(contentValue)}</div>`;
|
|
186
|
-
}
|
|
187
|
-
const content = toRecord(contentValue);
|
|
188
|
-
const html = toStringValue(content.html);
|
|
189
|
-
if (html) {
|
|
190
|
-
return state.options.allowUnsafeHtml
|
|
191
|
-
? html
|
|
192
|
-
: `<pre class="affine-zui-html-source"><code>${escapeHtml(html)}</code></pre>`;
|
|
193
|
-
}
|
|
194
|
-
const exported = Array.isArray(content.export) ? content.export : [];
|
|
195
|
-
if (exported.length) {
|
|
196
|
-
return `<div class="affine-zui-custom-block">${renderCustomExportNodes(exported, state)}</div>`;
|
|
197
|
-
}
|
|
198
|
-
const title = toStringValue(content.title);
|
|
199
|
-
const fetcher = content.fetcher;
|
|
200
|
-
const fetcherUrl = typeof fetcher === 'string' ? fetcher : toRecord(fetcher).url;
|
|
201
|
-
const url = sanitizeUrl(content.exportUrl) || sanitizeUrl(fetcherUrl);
|
|
202
|
-
const component = toStringValue(content.component);
|
|
203
|
-
return `<div class="affine-zui-custom-block">${title ? `<h5>${escapeHtml(title)}</h5>` : ''}${component
|
|
204
|
-
? `<div class="affine-zui-component-placeholder" data-component="${escapeHtmlAttribute(component)}">[Component: ${escapeHtml(component)}]</div>`
|
|
205
|
-
: ''}${url
|
|
206
|
-
? `<a href="${escapeHtmlAttribute(url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(url)}</a>`
|
|
207
|
-
: ''}</div>`;
|
|
208
|
-
}
|
|
209
|
-
function renderEmbedLink(block) {
|
|
210
|
-
const props = toRecord(block.props);
|
|
211
|
-
const url = sanitizeUrl(props.url);
|
|
212
|
-
if (!url)
|
|
213
|
-
return '';
|
|
214
|
-
const title = toStringValue(props.title) || url;
|
|
215
|
-
return `<div class="affine-paragraph-block-container"><a href="${escapeHtmlAttribute(url)}">${escapeHtml(title)}</a></div>`;
|
|
216
|
-
}
|
|
217
|
-
function renderHolder(block) {
|
|
218
|
-
const props = toRecord(block.props);
|
|
219
|
-
const text = toStringValue(props.text) || toStringValue(props.name);
|
|
220
|
-
return `<pre class="affine-zui-holder-block-container"${attribute('data-holder-name', props.name)}${dataAttribute('data-holder-data', props.data)}><code class="affine-zui-holder-block-content">${escapeHtml(text)}</code></pre>`;
|
|
221
|
-
}
|
|
222
|
-
function customRenderer(block, state) {
|
|
223
|
-
const renderer = state.options.renderBlock;
|
|
224
|
-
if (!renderer)
|
|
225
|
-
return undefined;
|
|
226
|
-
const context = {
|
|
227
|
-
format: 'html',
|
|
228
|
-
renderChildren: children => renderBlocks(children ?? blockChildren(block), state),
|
|
229
|
-
renderInline: text => deltaToHtml(text, state.options, block),
|
|
230
|
-
};
|
|
231
|
-
return renderer(block, context);
|
|
232
|
-
}
|
|
233
|
-
function renderUnknown(block, state) {
|
|
234
|
-
const strategy = state.options.unknownBlock ?? 'children';
|
|
235
|
-
if (strategy === 'throw') {
|
|
236
|
-
throw new TypeError(`Unsupported BlockSuite block flavour: ${block.flavour}`);
|
|
237
|
-
}
|
|
238
|
-
return strategy === 'children' ? renderBlocks(blockChildren(block), state) : '';
|
|
239
|
-
}
|
|
240
|
-
function renderBlock(block, state) {
|
|
241
|
-
const overridden = customRenderer(block, state);
|
|
242
|
-
if (overridden !== undefined)
|
|
243
|
-
return overridden;
|
|
244
|
-
const props = toRecord(block.props);
|
|
245
|
-
if (block.flavour === 'affine:page') {
|
|
246
|
-
return renderBlocks(blockChildren(block), state);
|
|
247
|
-
}
|
|
248
|
-
if (block.flavour === 'affine:note') {
|
|
249
|
-
return isEdgelessOnly(block) ? '' : renderBlocks(blockChildren(block), state);
|
|
250
|
-
}
|
|
251
|
-
if (TRANSPARENT_FLAVOURS.has(block.flavour)) {
|
|
252
|
-
return renderBlocks(blockChildren(block), state);
|
|
253
|
-
}
|
|
254
|
-
switch (block.flavour) {
|
|
255
|
-
case 'affine:paragraph': {
|
|
256
|
-
const text = deltaToHtml(props.text, state.options, block);
|
|
257
|
-
const type = toStringValue(props.type, 'text');
|
|
258
|
-
const align = safeAlign(props.align);
|
|
259
|
-
const style = align ? attribute('style', `text-align:${align}`) : '';
|
|
260
|
-
let own;
|
|
261
|
-
if (/^h[1-6]$/.test(type)) {
|
|
262
|
-
own = `<${type}${style}>${text}</${type}>`;
|
|
263
|
-
}
|
|
264
|
-
else if (type === 'quote') {
|
|
265
|
-
own = `<blockquote class="quote"><p${style}>${text}</p></blockquote>`;
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
own = `<p${style}>${text}</p>`;
|
|
269
|
-
}
|
|
270
|
-
const children = renderBlocks(blockChildren(block), state);
|
|
271
|
-
return `<div class="affine-paragraph-block-container">${own}${children
|
|
272
|
-
? `<div class="affine-block-children-container">${children}</div>`
|
|
273
|
-
: ''}</div>`;
|
|
274
|
-
}
|
|
275
|
-
case 'affine:code': {
|
|
276
|
-
const text = plainText(props.text);
|
|
277
|
-
const language = safeLanguage(props.language);
|
|
278
|
-
return `<pre><code${language ? ` class="language-${escapeHtmlAttribute(language)}"` : ''}>${escapeHtml(text)}</code></pre>`;
|
|
279
|
-
}
|
|
280
|
-
case 'affine:divider':
|
|
281
|
-
return '<hr>';
|
|
282
|
-
case 'affine:latex':
|
|
283
|
-
return `<div class="affine-latex-block" data-latex="${escapeHtmlAttribute(toStringValue(props.latex))}">${escapeHtml(toStringValue(props.latex))}</div>`;
|
|
284
|
-
case 'affine:image': {
|
|
285
|
-
const url = resolveAssetUrl(state.options, block, 'image');
|
|
286
|
-
if (!url)
|
|
287
|
-
return '';
|
|
288
|
-
const width = positiveInteger(props.width);
|
|
289
|
-
const height = positiveInteger(props.height);
|
|
290
|
-
const caption = toStringValue(props.caption);
|
|
291
|
-
return `<figure class="affine-image-block-container"><img src="${escapeHtmlAttribute(url)}" alt="${escapeHtmlAttribute(caption)}"${attribute('title', caption)}${attribute('width', width)}${attribute('height', height)}></figure>`;
|
|
292
|
-
}
|
|
293
|
-
case 'affine:zui-image': {
|
|
294
|
-
const url = resolveZuiImageUrl(state.options, block);
|
|
295
|
-
if (!url)
|
|
296
|
-
return '';
|
|
297
|
-
return `<figure class="affine-image-block-container"${safeAlign(props.align)
|
|
298
|
-
? attribute('style', `text-align:${safeAlign(props.align)}`)
|
|
299
|
-
: ''}><img src="${escapeHtmlAttribute(url)}" alt="${escapeHtmlAttribute(toStringValue(props.caption))}"${attribute('width', positiveInteger(props.width))}${attribute('height', positiveInteger(props.height))}></figure>`;
|
|
300
|
-
}
|
|
301
|
-
case 'affine:attachment': {
|
|
302
|
-
const name = toStringValue(props.name, 'Attachment');
|
|
303
|
-
const url = resolveAssetUrl(state.options, block, 'attachment');
|
|
304
|
-
const caption = toStringValue(props.caption);
|
|
305
|
-
const label = url
|
|
306
|
-
? `<a href="${escapeHtmlAttribute(url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(name)}</a>`
|
|
307
|
-
: escapeHtml(name);
|
|
308
|
-
return `<div class="affine-attachment-block-container"><p><strong>${label}</strong> <em>(${escapeHtml(formatFileSize(props.size))}${caption ? `, ${escapeHtml(caption)}` : ''})</em></p></div>`;
|
|
309
|
-
}
|
|
310
|
-
case 'affine:database':
|
|
311
|
-
return renderDatabase(block, state);
|
|
312
|
-
case 'affine:zui-table':
|
|
313
|
-
return renderZuiTable(block, state);
|
|
314
|
-
case 'affine:zui-holder':
|
|
315
|
-
return joinHtml([
|
|
316
|
-
renderHolder(block),
|
|
317
|
-
renderBlocks(blockChildren(block), state),
|
|
318
|
-
]);
|
|
319
|
-
case 'affine:zui-layout':
|
|
320
|
-
return `<div class="affine-zui-layout"${attribute('data-layout', props.type)}>${renderBlocks(blockChildren(block), state)}</div>`;
|
|
321
|
-
case 'affine:zui-panel':
|
|
322
|
-
return `<aside class="affine-zui-panel"${attribute('data-type', props.type)}>${renderBlocks(blockChildren(block), state)}</aside>`;
|
|
323
|
-
case 'affine:zui-expand': {
|
|
324
|
-
const title = deltaToHtml(props.title, state.options, block);
|
|
325
|
-
return `<details class="affine-zui-expand"${props.open === false ? '' : ' open'}><summary>${title}</summary>${renderBlocks(blockChildren(block), state)}</details>`;
|
|
326
|
-
}
|
|
327
|
-
case 'affine:embed-zui-whiteboard': {
|
|
328
|
-
const image = sanitizeUrl(props.sceneImage, 'image');
|
|
329
|
-
if (image) {
|
|
330
|
-
return `<img class="affine-whiteboard-block-image" src="${escapeHtmlAttribute(image)}" alt="${escapeHtmlAttribute(toStringValue(props.caption, 'whiteboard'))}">`;
|
|
331
|
-
}
|
|
332
|
-
return `<div class="affine-whiteboard-block-container">${props.sceneData ? '[Whiteboard]' : '[Empty Whiteboard]'}</div>`;
|
|
333
|
-
}
|
|
334
|
-
case 'affine:embed-zui-custom':
|
|
335
|
-
return renderCustomBlock(block, state);
|
|
336
|
-
case 'affine:embed-zui-html': {
|
|
337
|
-
const html = toStringValue(props.html);
|
|
338
|
-
if (!html)
|
|
339
|
-
return '';
|
|
340
|
-
return state.options.allowUnsafeHtml
|
|
341
|
-
? html
|
|
342
|
-
: `<pre class="affine-zui-html-source"><code>${escapeHtml(html)}</code></pre>`;
|
|
343
|
-
}
|
|
344
|
-
case 'affine:embed-zui-iframe': {
|
|
345
|
-
const url = sanitizeUrl(props.src);
|
|
346
|
-
if (!url)
|
|
347
|
-
return '';
|
|
348
|
-
const sandbox = toStringValue(props.sandbox);
|
|
349
|
-
let sandboxTokens = sandbox
|
|
350
|
-
.split(/\s+/)
|
|
351
|
-
.filter(token => [
|
|
352
|
-
'allow-forms',
|
|
353
|
-
'allow-modals',
|
|
354
|
-
'allow-pointer-lock',
|
|
355
|
-
'allow-popups',
|
|
356
|
-
'allow-same-origin',
|
|
357
|
-
'allow-scripts',
|
|
358
|
-
].includes(token));
|
|
359
|
-
// A same-origin scripted frame can remove its own sandbox attribute.
|
|
360
|
-
if (sandboxTokens.includes('allow-scripts') &&
|
|
361
|
-
sandboxTokens.includes('allow-same-origin')) {
|
|
362
|
-
sandboxTokens = sandboxTokens.filter(token => token !== 'allow-same-origin');
|
|
363
|
-
}
|
|
364
|
-
const sandboxAttribute = sandboxTokens.length
|
|
365
|
-
? attribute('sandbox', sandboxTokens.join(' '))
|
|
366
|
-
: ' sandbox';
|
|
367
|
-
return `<iframe src="${escapeHtmlAttribute(url)}"${sandboxAttribute}${attribute('width', safeDimension(props.width))}${attribute('height', safeDimension(props.height))}${props.allowfullscreen ? ' allowfullscreen' : ''}></iframe>`;
|
|
368
|
-
}
|
|
369
|
-
case 'affine:embed-zui-component': {
|
|
370
|
-
const name = toStringValue(props.name);
|
|
371
|
-
return name
|
|
372
|
-
? `<div class="affine-zui-component-placeholder" data-component="${escapeHtmlAttribute(name)}">[Component: ${escapeHtml(name)}]</div>`
|
|
373
|
-
: '';
|
|
374
|
-
}
|
|
375
|
-
case 'affine:embed-linked-doc': {
|
|
376
|
-
const pageId = toStringValue(props.pageId);
|
|
377
|
-
if (!pageId)
|
|
378
|
-
return '';
|
|
379
|
-
const title = state.options.resolveDocTitle?.(pageId) ?? 'untitled';
|
|
380
|
-
const url = resolveDocumentUrl(state.options, { pageId, params: toRecord(props.params), title }, block);
|
|
381
|
-
return url
|
|
382
|
-
? `<a href="${escapeHtmlAttribute(url)}">${escapeHtml(title)}</a>`
|
|
383
|
-
: escapeHtml(title);
|
|
384
|
-
}
|
|
385
|
-
case 'affine:embed-synced-doc': {
|
|
386
|
-
const children = blockChildren(block);
|
|
387
|
-
if (children.length)
|
|
388
|
-
return renderBlocks(children, state);
|
|
389
|
-
const pageId = toStringValue(props.pageId);
|
|
390
|
-
if (!pageId)
|
|
391
|
-
return '';
|
|
392
|
-
const title = state.options.resolveDocTitle?.(pageId) ?? pageId;
|
|
393
|
-
const url = resolveDocumentUrl(state.options, { pageId, params: toRecord(props.params), title }, block);
|
|
394
|
-
return url
|
|
395
|
-
? `<a class="affine-synced-doc-link" href="${escapeHtmlAttribute(url)}">${escapeHtml(title)}</a>`
|
|
396
|
-
: `<span class="affine-synced-doc-link">${escapeHtml(title)}</span>`;
|
|
397
|
-
}
|
|
398
|
-
default:
|
|
399
|
-
return EMBED_LINK_FLAVOURS.has(block.flavour)
|
|
400
|
-
? renderEmbedLink(block)
|
|
401
|
-
: renderUnknown(block, state);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
function renderBlocks(blocks, state) {
|
|
405
|
-
const rendered = [];
|
|
406
|
-
for (let index = 0; index < blocks.length; index += 1) {
|
|
407
|
-
const block = blocks[index];
|
|
408
|
-
if (!block)
|
|
409
|
-
continue;
|
|
410
|
-
if (block.flavour === 'affine:list') {
|
|
411
|
-
const type = toStringValue(toRecord(block.props).type, 'bulleted');
|
|
412
|
-
const group = [block];
|
|
413
|
-
while (index + 1 < blocks.length) {
|
|
414
|
-
const next = blocks[index + 1];
|
|
415
|
-
if (next?.flavour !== 'affine:list' ||
|
|
416
|
-
toStringValue(toRecord(next.props).type, 'bulleted') !== type) {
|
|
417
|
-
break;
|
|
418
|
-
}
|
|
419
|
-
group.push(next);
|
|
420
|
-
index += 1;
|
|
421
|
-
}
|
|
422
|
-
rendered.push(renderListGroup(group, state));
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
rendered.push(renderBlock(block, state));
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
return joinHtml(rendered);
|
|
429
|
-
}
|
|
430
|
-
/** Convert a BlockSuite 0.19.x snapshot to semantic, escaped HTML. */
|
|
431
|
-
export function snapshotToHtml(input, options = {}) {
|
|
432
|
-
const normalized = normalizeSnapshot(input);
|
|
433
|
-
validateSnapshotTree(normalized.blocks, options);
|
|
434
|
-
const state = { options };
|
|
435
|
-
const title = pageTitleText(normalized);
|
|
436
|
-
const heading = options.includeTitle === false || !title ? '' : `<h1>${escapeHtml(title)}</h1>`;
|
|
437
|
-
const body = joinHtml([heading, renderBlocks(normalized.blocks, state)]);
|
|
438
|
-
const fullDocument = options.fullDocument ?? normalized.documentLike;
|
|
439
|
-
if (!fullDocument)
|
|
440
|
-
return body;
|
|
441
|
-
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escapeHtml(title)}</title></head><body><main class="blocksuite-document">${body}</main></body></html>`;
|
|
442
|
-
}
|