@asteroidcms/core-utils 0.1.2 → 0.1.4
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 +59 -4
- package/dist/client.cjs +1684 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +316 -0
- package/dist/client.d.ts +316 -0
- package/dist/client.js +1669 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +99 -740
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -180
- package/dist/index.d.ts +42 -180
- package/dist/index.js +97 -731
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
package/dist/index.cjs
CHANGED
|
@@ -1,29 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var react = require('react');
|
|
4
|
-
var react$1 = require('@apollo/client/react');
|
|
5
3
|
var client = require('@apollo/client');
|
|
6
4
|
var context = require('@apollo/client/link/context');
|
|
7
5
|
var error = require('@apollo/client/link/error');
|
|
8
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
-
var hljs = require('highlight.js/lib/common');
|
|
10
|
-
require('highlight.js/styles/tokyo-night-dark.css');
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
var hljs__default = /*#__PURE__*/_interopDefault(hljs);
|
|
15
|
-
|
|
16
|
-
// src/provider/AsteroidCMSProvider.tsx
|
|
17
|
-
var AsteroidCMSContext = react.createContext(null);
|
|
18
|
-
function useAsteroidCMSConfig() {
|
|
19
|
-
const ctx = react.useContext(AsteroidCMSContext);
|
|
20
|
-
if (!ctx) {
|
|
21
|
-
throw new Error(
|
|
22
|
-
"useAsteroidCMSConfig must be used inside <AsteroidCMSProvider>. Wrap your app with <AsteroidCMSProvider cmsUrl=... apiKey=... />."
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
return ctx;
|
|
26
|
-
}
|
|
7
|
+
// src/apollo/createApolloClient.ts
|
|
27
8
|
function createErrorLink(onError) {
|
|
28
9
|
return new error.ErrorLink(({ error }) => {
|
|
29
10
|
if (!onError) return;
|
|
@@ -83,234 +64,6 @@ function createApolloClient(config) {
|
|
|
83
64
|
...config.apolloOptions
|
|
84
65
|
});
|
|
85
66
|
}
|
|
86
|
-
function AsteroidCMSProvider({ children, ...config }) {
|
|
87
|
-
const resolved = react.useMemo(() => resolveConfig(config), [
|
|
88
|
-
config.cmsUrl,
|
|
89
|
-
config.apiKey,
|
|
90
|
-
config.graphqlPath,
|
|
91
|
-
config.mediaPath,
|
|
92
|
-
config.headers,
|
|
93
|
-
config.onError
|
|
94
|
-
]);
|
|
95
|
-
const client = react.useMemo(
|
|
96
|
-
() => createApolloClient(config),
|
|
97
|
-
// The client is intentionally rebuilt only when the identity-shaping props change.
|
|
98
|
-
[
|
|
99
|
-
config.client,
|
|
100
|
-
resolved.cmsUrl,
|
|
101
|
-
resolved.apiKey,
|
|
102
|
-
resolved.graphqlPath,
|
|
103
|
-
config.cacheConfig,
|
|
104
|
-
config.apolloOptions
|
|
105
|
-
]
|
|
106
|
-
);
|
|
107
|
-
return /* @__PURE__ */ jsxRuntime.jsx(AsteroidCMSContext.Provider, { value: resolved, children: /* @__PURE__ */ jsxRuntime.jsx(react$1.ApolloProvider, { client, children }) });
|
|
108
|
-
}
|
|
109
|
-
function useCmsContent({
|
|
110
|
-
schema_slug,
|
|
111
|
-
entrySlug,
|
|
112
|
-
select = [],
|
|
113
|
-
fullData = false,
|
|
114
|
-
limit,
|
|
115
|
-
offset,
|
|
116
|
-
status = "PUBLISHED",
|
|
117
|
-
filter,
|
|
118
|
-
search,
|
|
119
|
-
variables = {}
|
|
120
|
-
}) {
|
|
121
|
-
const isSingle = !!entrySlug;
|
|
122
|
-
const buildSelection2 = (items = []) => {
|
|
123
|
-
const lines = [];
|
|
124
|
-
items.forEach((item) => {
|
|
125
|
-
if (typeof item === "string") {
|
|
126
|
-
lines.push(`${item}: dataField(slug: "${item}")`);
|
|
127
|
-
} else if ("field" in item && typeof item.field === "string") {
|
|
128
|
-
if (!("select" in item)) {
|
|
129
|
-
const alias2 = item.as || item.field;
|
|
130
|
-
lines.push(`${alias2}: dataField(slug: "${item.field}")`);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
const alias = item.as || item.field;
|
|
134
|
-
const resolver = item.single ? "expandedReferenceObject" : "expandedReference";
|
|
135
|
-
const subSelection = item.select?.length ? buildSelection2(item.select) : "data { ... }";
|
|
136
|
-
lines.push(`
|
|
137
|
-
${alias}: ${resolver}(slug: "${item.field}") {
|
|
138
|
-
${subSelection}
|
|
139
|
-
}
|
|
140
|
-
`);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
return lines.join("\n ").trim();
|
|
144
|
-
};
|
|
145
|
-
const selectionParts = [];
|
|
146
|
-
if (fullData) selectionParts.push("data");
|
|
147
|
-
if (select.length > 0) {
|
|
148
|
-
const userSelection = buildSelection2(select);
|
|
149
|
-
if (userSelection) selectionParts.push(userSelection);
|
|
150
|
-
}
|
|
151
|
-
const selection = selectionParts.join("\n ").trim() || "id";
|
|
152
|
-
const queryVariables = {
|
|
153
|
-
schema_slug,
|
|
154
|
-
...entrySlug && { slug: entrySlug },
|
|
155
|
-
...variables
|
|
156
|
-
};
|
|
157
|
-
const fieldArgParts = ["schema_slug: $schema_slug"];
|
|
158
|
-
if (!isSingle) {
|
|
159
|
-
if (typeof limit === "number" && limit >= 0) {
|
|
160
|
-
fieldArgParts.push("limit: $limit");
|
|
161
|
-
queryVariables.limit = limit;
|
|
162
|
-
}
|
|
163
|
-
if (typeof offset === "number" && offset >= 0) {
|
|
164
|
-
fieldArgParts.push("offset: $offset");
|
|
165
|
-
queryVariables.offset = offset;
|
|
166
|
-
}
|
|
167
|
-
if (status) {
|
|
168
|
-
fieldArgParts.push("status: $status");
|
|
169
|
-
queryVariables.status = status;
|
|
170
|
-
}
|
|
171
|
-
const hasSearch = Array.isArray(search) && search.length > 0;
|
|
172
|
-
const hasFilter = filter && typeof filter === "object" && Object.keys(filter).length > 0;
|
|
173
|
-
if (hasSearch || hasFilter) {
|
|
174
|
-
const mergedFilter = hasFilter ? { ...filter } : {};
|
|
175
|
-
if (hasSearch) {
|
|
176
|
-
for (const condition of search) {
|
|
177
|
-
mergedFilter[condition.field] = {
|
|
178
|
-
regex: true,
|
|
179
|
-
value: condition.value,
|
|
180
|
-
mode: condition.mode ?? "i"
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
fieldArgParts.push("data: $filter");
|
|
185
|
-
queryVariables.filter = mergedFilter;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
const varDecls = ["$schema_slug: String!"];
|
|
189
|
-
if ("limit" in queryVariables) varDecls.push("$limit: Float");
|
|
190
|
-
if ("offset" in queryVariables) varDecls.push("$offset: Float");
|
|
191
|
-
if ("status" in queryVariables) varDecls.push("$status: ContentStatus");
|
|
192
|
-
if ("filter" in queryVariables) varDecls.push("$filter: JSONObject");
|
|
193
|
-
const varBlock = varDecls.length > 0 ? `(
|
|
194
|
-
${varDecls.join("\n ")}
|
|
195
|
-
)` : "";
|
|
196
|
-
const contentFieldArgs = fieldArgParts.join(", ");
|
|
197
|
-
const queryStr = isSingle ? `
|
|
198
|
-
query Get${schema_slug}Entry($schema_slug: String!, $slug: String!) {
|
|
199
|
-
entry: contentEntry(schema_slug: $schema_slug, slug: $slug) {
|
|
200
|
-
${selection}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
` : `
|
|
204
|
-
query Get${schema_slug}Entries${varBlock} {
|
|
205
|
-
entries: contentEntries(${contentFieldArgs}) {
|
|
206
|
-
${selection}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
`;
|
|
210
|
-
const GET_CONTENT = client.gql(queryStr);
|
|
211
|
-
const { loading, error, data, ...rest } = react$1.useQuery(GET_CONTENT, {
|
|
212
|
-
variables: queryVariables,
|
|
213
|
-
skip: !schema_slug || isSingle && !entrySlug
|
|
214
|
-
});
|
|
215
|
-
const typed = data;
|
|
216
|
-
const result = isSingle ? typed?.entry : typed?.entries;
|
|
217
|
-
return {
|
|
218
|
-
loading,
|
|
219
|
-
error,
|
|
220
|
-
data: result,
|
|
221
|
-
...rest
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
function useCmsMutate({
|
|
225
|
-
schema_slug,
|
|
226
|
-
select = [],
|
|
227
|
-
fullData = false,
|
|
228
|
-
mutationType = "create",
|
|
229
|
-
entryId,
|
|
230
|
-
variables: inputVariables = {}
|
|
231
|
-
}) {
|
|
232
|
-
const buildSelection2 = (items = []) => {
|
|
233
|
-
const lines = [];
|
|
234
|
-
for (const item of items) {
|
|
235
|
-
if (typeof item === "string") {
|
|
236
|
-
lines.push(`${item}: dataField(slug: "${item}")`);
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
const { field, as, single, select: subSelect } = item;
|
|
240
|
-
const alias = as || field;
|
|
241
|
-
if (!subSelect?.length) {
|
|
242
|
-
lines.push(`${alias}: dataField(slug: "${field}")`);
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
const resolver = single ? "expandedReferenceObject" : "expandedReference";
|
|
246
|
-
const sub = buildSelection2(subSelect) || "id slug";
|
|
247
|
-
lines.push(`
|
|
248
|
-
${alias}: ${resolver}(slug: "${field}") {
|
|
249
|
-
id
|
|
250
|
-
slug
|
|
251
|
-
${sub}
|
|
252
|
-
}
|
|
253
|
-
`);
|
|
254
|
-
}
|
|
255
|
-
return lines.join("\n ").trim();
|
|
256
|
-
};
|
|
257
|
-
const userSelection = buildSelection2(select);
|
|
258
|
-
let selection = fullData ? "data" : "";
|
|
259
|
-
if (userSelection) {
|
|
260
|
-
selection = selection ? `${selection}
|
|
261
|
-
${userSelection}` : userSelection;
|
|
262
|
-
}
|
|
263
|
-
if (!selection) {
|
|
264
|
-
selection = "id slug status version created_at updated_at";
|
|
265
|
-
}
|
|
266
|
-
const operation = mutationType;
|
|
267
|
-
const mutationName = `${operation}ContentEntry`;
|
|
268
|
-
const varDecls = ["$schema_slug: String!"];
|
|
269
|
-
const callArgs = ["schema_slug: $schema_slug"];
|
|
270
|
-
if (operation === "create" || operation === "update") {
|
|
271
|
-
varDecls.unshift("$data: JSONObject!");
|
|
272
|
-
callArgs.unshift("data: $data");
|
|
273
|
-
}
|
|
274
|
-
if (operation === "update" || operation === "delete") {
|
|
275
|
-
varDecls.push("$id: ID!");
|
|
276
|
-
callArgs.push("id: $id");
|
|
277
|
-
}
|
|
278
|
-
const returnFields = selection.includes("data") ? selection : `${selection}
|
|
279
|
-
data`;
|
|
280
|
-
const MUTATION = client.gql`
|
|
281
|
-
mutation ${mutationName}(${varDecls.join(", ")}) {
|
|
282
|
-
result: ${mutationName}(${callArgs.join(", ")}) {
|
|
283
|
-
id
|
|
284
|
-
status
|
|
285
|
-
version
|
|
286
|
-
created_at
|
|
287
|
-
updated_at
|
|
288
|
-
schema {
|
|
289
|
-
id
|
|
290
|
-
slug
|
|
291
|
-
}
|
|
292
|
-
${returnFields}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
`;
|
|
296
|
-
const mutationVariables = {
|
|
297
|
-
schema_slug,
|
|
298
|
-
...operation === "update" || operation === "delete" ? { id: entryId } : {},
|
|
299
|
-
...inputVariables
|
|
300
|
-
};
|
|
301
|
-
if ((operation === "update" || operation === "delete") && !entryId) {
|
|
302
|
-
console.warn(`useCmsMutate: entryId is required for ${operation}`);
|
|
303
|
-
}
|
|
304
|
-
const [mutateFn, mutationResult] = react$1.useMutation(MUTATION, {
|
|
305
|
-
variables: mutationVariables
|
|
306
|
-
});
|
|
307
|
-
const resultData = mutationResult.data?.result;
|
|
308
|
-
return {
|
|
309
|
-
mutate: mutateFn,
|
|
310
|
-
...mutationResult,
|
|
311
|
-
data: resultData
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
67
|
function buildSelection(items = []) {
|
|
315
68
|
const lines = [];
|
|
316
69
|
items.forEach((item) => {
|
|
@@ -442,10 +195,6 @@ function cmsImage(id, options) {
|
|
|
442
195
|
const path = (options.mediaPath ?? "/media/canonical").replace(/^\/?/, "/");
|
|
443
196
|
return `${base}${path}/${id}`;
|
|
444
197
|
}
|
|
445
|
-
function useCmsImage() {
|
|
446
|
-
const { cmsUrl, mediaPath } = useAsteroidCMSConfig();
|
|
447
|
-
return (id) => cmsImage(id, { cmsUrl, mediaPath });
|
|
448
|
-
}
|
|
449
198
|
|
|
450
199
|
// src/utils/getContentReadTime.ts
|
|
451
200
|
function getContentReadTime(content, options = {}) {
|
|
@@ -577,7 +326,36 @@ function parseRichText(html, options = {}) {
|
|
|
577
326
|
working = upgradeStandaloneImages(working);
|
|
578
327
|
working = upgradeAuthoredBlockquotes(working);
|
|
579
328
|
working = flattenTableCellParagraphs(working);
|
|
580
|
-
|
|
329
|
+
working = sanitizeAndStyle(working, options);
|
|
330
|
+
if (options.autoHeadingIds !== false) {
|
|
331
|
+
working = injectHeadingIds(working);
|
|
332
|
+
}
|
|
333
|
+
return working;
|
|
334
|
+
}
|
|
335
|
+
function injectHeadingIds(html) {
|
|
336
|
+
const used = /* @__PURE__ */ new Map();
|
|
337
|
+
const existingRe = /<h[1-6]\b[^>]*\bid\s*=\s*("([^"]*)"|'([^']*)'|(\S+))/gi;
|
|
338
|
+
let em;
|
|
339
|
+
while ((em = existingRe.exec(html)) !== null) {
|
|
340
|
+
const id = em[2] ?? em[3] ?? em[4] ?? "";
|
|
341
|
+
if (id) used.set(id, (used.get(id) ?? 0) + 1);
|
|
342
|
+
}
|
|
343
|
+
return html.replace(
|
|
344
|
+
/<(h[1-6])\b([^>]*)>([\s\S]*?)<\/\1>/gi,
|
|
345
|
+
(full, tag, attrs, inner) => {
|
|
346
|
+
if (/\bid\s*=/i.test(attrs)) return full;
|
|
347
|
+
const text = inner.replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/\s+/g, " ").trim();
|
|
348
|
+
if (!text) return full;
|
|
349
|
+
const base = slugifyHeading(text) || tag;
|
|
350
|
+
const n = used.get(base) ?? 0;
|
|
351
|
+
used.set(base, n + 1);
|
|
352
|
+
const id = n === 0 ? base : `${base}-${n}`;
|
|
353
|
+
return `<${tag}${attrs} id="${id}">${inner}</${tag}>`;
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
function slugifyHeading(text) {
|
|
358
|
+
return text.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().trim().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
581
359
|
}
|
|
582
360
|
function flattenTableCellParagraphs(html) {
|
|
583
361
|
return html.replace(
|
|
@@ -1119,507 +897,88 @@ function sanitizeAndStyle(html, options) {
|
|
|
1119
897
|
return out.join("");
|
|
1120
898
|
}
|
|
1121
899
|
|
|
1122
|
-
// src/
|
|
1123
|
-
var
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
}
|
|
1133
|
-
function
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
if (bq.dataset.rtQuoted === "1") return;
|
|
1161
|
-
if (bq.closest('figure[data-variant="pullquote"]')) return;
|
|
1162
|
-
bq.dataset.rtQuoted = "1";
|
|
1163
|
-
const { first, last } = findQuoteBody(bq);
|
|
1164
|
-
if (!first || !last) return;
|
|
1165
|
-
const open = document.createElement("span");
|
|
1166
|
-
open.className = "rt-quote-open";
|
|
1167
|
-
open.setAttribute("aria-hidden", "true");
|
|
1168
|
-
open.textContent = "\u201C";
|
|
1169
|
-
const close = document.createElement("span");
|
|
1170
|
-
close.className = "rt-quote-close";
|
|
1171
|
-
close.setAttribute("aria-hidden", "true");
|
|
1172
|
-
close.textContent = "\u201D";
|
|
1173
|
-
first.prepend(open);
|
|
1174
|
-
last.append(close);
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
function escapeHtml2(s) {
|
|
1178
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1179
|
-
}
|
|
1180
|
-
function preserveSpaces(html) {
|
|
1181
|
-
return html.replace(/ {2,}/g, (m) => " ".repeat(m.length));
|
|
1182
|
-
}
|
|
1183
|
-
function highlightSource(src, lang) {
|
|
1184
|
-
if (lang && hljs__default.default.getLanguage(lang)) {
|
|
1185
|
-
try {
|
|
1186
|
-
return hljs__default.default.highlight(src, { language: lang, ignoreIllegals: true }).value;
|
|
1187
|
-
} catch {
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
return escapeHtml2(src);
|
|
1191
|
-
}
|
|
1192
|
-
function highlightLine(line, lang) {
|
|
1193
|
-
if (!line) return "";
|
|
1194
|
-
return preserveSpaces(highlightSource(line, lang));
|
|
1195
|
-
}
|
|
1196
|
-
function highlightCodeBlock(pre) {
|
|
1197
|
-
const lang = pre.dataset.language;
|
|
1198
|
-
if (!lang) return;
|
|
1199
|
-
const code = pre.querySelector("code");
|
|
1200
|
-
if (!code) return;
|
|
1201
|
-
if (code.dataset.rtHighlighted === "1") return;
|
|
1202
|
-
const source = code.textContent ?? "";
|
|
1203
|
-
code.innerHTML = highlightSource(source, lang);
|
|
1204
|
-
code.classList.add("hljs");
|
|
1205
|
-
code.dataset.rtHighlighted = "1";
|
|
1206
|
-
}
|
|
1207
|
-
var DIFF_SEPARATOR_RE = /\n?@@---@@\n?/;
|
|
1208
|
-
function diffLines(a, b) {
|
|
1209
|
-
const n = a.length;
|
|
1210
|
-
const m = b.length;
|
|
1211
|
-
const dp = Array.from(
|
|
1212
|
-
{ length: n + 1 },
|
|
1213
|
-
() => new Array(m + 1).fill(0)
|
|
1214
|
-
);
|
|
1215
|
-
for (let i2 = 1; i2 <= n; i2++) {
|
|
1216
|
-
for (let j2 = 1; j2 <= m; j2++) {
|
|
1217
|
-
dp[i2][j2] = a[i2 - 1] === b[j2 - 1] ? dp[i2 - 1][j2 - 1] + 1 : Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
const ops = [];
|
|
1221
|
-
let i = n;
|
|
1222
|
-
let j = m;
|
|
1223
|
-
while (i > 0 && j > 0) {
|
|
1224
|
-
if (a[i - 1] === b[j - 1]) {
|
|
1225
|
-
ops.unshift({ t: "eq", line: a[i - 1] });
|
|
1226
|
-
i--;
|
|
1227
|
-
j--;
|
|
1228
|
-
} else if (dp[i - 1][j] >= dp[i][j - 1]) {
|
|
1229
|
-
ops.unshift({ t: "rem", line: a[i - 1] });
|
|
1230
|
-
i--;
|
|
900
|
+
// src/utils/extractHeadings.ts
|
|
901
|
+
var DEFAULT_LEVELS = [2, 3];
|
|
902
|
+
function slugify(text) {
|
|
903
|
+
return text.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().trim().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
904
|
+
}
|
|
905
|
+
function decodeBasicEntities(s) {
|
|
906
|
+
return s.replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
907
|
+
}
|
|
908
|
+
function stripTags(s) {
|
|
909
|
+
return decodeBasicEntities(s.replace(/<[^>]+>/g, " ")).replace(/\s+/g, " ").trim();
|
|
910
|
+
}
|
|
911
|
+
function uniqueId(base, used) {
|
|
912
|
+
const seed = base || "section";
|
|
913
|
+
const n = used.get(seed) ?? 0;
|
|
914
|
+
used.set(seed, n + 1);
|
|
915
|
+
return n === 0 ? seed : `${seed}-${n}`;
|
|
916
|
+
}
|
|
917
|
+
function extractHeadingsFromHtml(html, options = {}) {
|
|
918
|
+
if (!html) return [];
|
|
919
|
+
const levels = options.levels ?? DEFAULT_LEVELS;
|
|
920
|
+
const slug = options.slugify ?? slugify;
|
|
921
|
+
const used = /* @__PURE__ */ new Map();
|
|
922
|
+
const out = [];
|
|
923
|
+
const re = /<h([1-6])\b([^>]*)>([\s\S]*?)<\/h\1>/gi;
|
|
924
|
+
let m;
|
|
925
|
+
let i = 0;
|
|
926
|
+
while ((m = re.exec(html)) !== null) {
|
|
927
|
+
const level = Number(m[1]);
|
|
928
|
+
if (!levels.includes(level)) continue;
|
|
929
|
+
const attrs = m[2] ?? "";
|
|
930
|
+
const inner = m[3] ?? "";
|
|
931
|
+
const text = stripTags(inner);
|
|
932
|
+
if (!text) continue;
|
|
933
|
+
const explicitIdMatch = attrs.match(/\bid\s*=\s*("([^"]*)"|'([^']*)'|(\S+))/i);
|
|
934
|
+
let id;
|
|
935
|
+
if (explicitIdMatch) {
|
|
936
|
+
id = explicitIdMatch[2] ?? explicitIdMatch[3] ?? explicitIdMatch[4] ?? "";
|
|
937
|
+
if (id) used.set(id, (used.get(id) ?? 0) + 1);
|
|
1231
938
|
} else {
|
|
1232
|
-
|
|
1233
|
-
j--;
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
while (i > 0) ops.unshift({ t: "rem", line: a[--i] });
|
|
1237
|
-
while (j > 0) ops.unshift({ t: "add", line: b[--j] });
|
|
1238
|
-
return ops;
|
|
1239
|
-
}
|
|
1240
|
-
function renderDiff(before, after, lang) {
|
|
1241
|
-
const ops = diffLines(before.split("\n"), after.split("\n"));
|
|
1242
|
-
const rows = [];
|
|
1243
|
-
let aNo = 0;
|
|
1244
|
-
let bNo = 0;
|
|
1245
|
-
let k = 0;
|
|
1246
|
-
while (k < ops.length) {
|
|
1247
|
-
const op = ops[k];
|
|
1248
|
-
if (op.t === "eq") {
|
|
1249
|
-
aNo++;
|
|
1250
|
-
bNo++;
|
|
1251
|
-
rows.push({
|
|
1252
|
-
left: { n: aNo, line: op.line, cls: "" },
|
|
1253
|
-
right: { n: bNo, line: op.line, cls: "" }
|
|
1254
|
-
});
|
|
1255
|
-
k++;
|
|
1256
|
-
continue;
|
|
1257
|
-
}
|
|
1258
|
-
const rems = [];
|
|
1259
|
-
while (k < ops.length && ops[k].t === "rem") {
|
|
1260
|
-
rems.push(ops[k].line);
|
|
1261
|
-
k++;
|
|
1262
|
-
}
|
|
1263
|
-
const adds = [];
|
|
1264
|
-
while (k < ops.length && ops[k].t === "add") {
|
|
1265
|
-
adds.push(ops[k].line);
|
|
1266
|
-
k++;
|
|
1267
|
-
}
|
|
1268
|
-
const len = Math.max(rems.length, adds.length);
|
|
1269
|
-
for (let x = 0; x < len; x++) {
|
|
1270
|
-
const remLine = x < rems.length ? rems[x] : null;
|
|
1271
|
-
const addLine = x < adds.length ? adds[x] : null;
|
|
1272
|
-
rows.push({
|
|
1273
|
-
left: {
|
|
1274
|
-
n: remLine !== null ? ++aNo : null,
|
|
1275
|
-
line: remLine,
|
|
1276
|
-
cls: remLine !== null ? "rt-diff-rem" : "rt-diff-empty"
|
|
1277
|
-
},
|
|
1278
|
-
right: {
|
|
1279
|
-
n: addLine !== null ? ++bNo : null,
|
|
1280
|
-
line: addLine,
|
|
1281
|
-
cls: addLine !== null ? "rt-diff-add" : "rt-diff-empty"
|
|
1282
|
-
}
|
|
1283
|
-
});
|
|
939
|
+
id = uniqueId(slug(text, i), used);
|
|
1284
940
|
}
|
|
941
|
+
out.push({ id, text, level });
|
|
942
|
+
i++;
|
|
1285
943
|
}
|
|
1286
|
-
|
|
1287
|
-
const sign = side === "l" ? "-" : "+";
|
|
1288
|
-
const showSign = c.cls === "rt-diff-rem" || c.cls === "rt-diff-add";
|
|
1289
|
-
return `<td class="rt-diff-num">${c.n ?? ""}</td><td class="rt-diff-sign">${showSign ? sign : ""}</td><td class="rt-diff-line ${c.cls}">${c.line === null ? "" : highlightLine(c.line, lang) || " "}</td>`;
|
|
1290
|
-
};
|
|
1291
|
-
const body = rows.map((r) => `<tr>${cell(r.left, "l")}${cell(r.right, "r")}</tr>`).join("");
|
|
1292
|
-
return `<table class="rt-diff-table"><tbody>${body}</tbody></table>`;
|
|
1293
|
-
}
|
|
1294
|
-
function makeCopyButton(getText, opts = {}) {
|
|
1295
|
-
const btn = document.createElement("button");
|
|
1296
|
-
btn.type = "button";
|
|
1297
|
-
btn.className = opts.className ?? "rt-codeblock__copy";
|
|
1298
|
-
btn.setAttribute("aria-label", opts.label ?? "Copy code");
|
|
1299
|
-
btn.innerHTML = COPY_ICON_SVG;
|
|
1300
|
-
btn.addEventListener("click", async () => {
|
|
1301
|
-
try {
|
|
1302
|
-
await navigator.clipboard.writeText(getText());
|
|
1303
|
-
btn.innerHTML = CHECK_ICON_SVG;
|
|
1304
|
-
btn.classList.add("is-copied");
|
|
1305
|
-
window.setTimeout(() => {
|
|
1306
|
-
btn.innerHTML = COPY_ICON_SVG;
|
|
1307
|
-
btn.classList.remove("is-copied");
|
|
1308
|
-
}, 1500);
|
|
1309
|
-
} catch {
|
|
1310
|
-
}
|
|
1311
|
-
});
|
|
1312
|
-
return btn;
|
|
1313
|
-
}
|
|
1314
|
-
function buildCodeBlockLabel(pre) {
|
|
1315
|
-
const filename = pre.dataset.filename;
|
|
1316
|
-
if (!filename) return null;
|
|
1317
|
-
const tag = document.createElement("span");
|
|
1318
|
-
tag.className = "rt-codeblock__label";
|
|
1319
|
-
const file = document.createElement("span");
|
|
1320
|
-
file.className = "rt-codeblock__filename rt-codeblock__language";
|
|
1321
|
-
file.textContent = filename;
|
|
1322
|
-
tag.appendChild(file);
|
|
1323
|
-
return tag;
|
|
944
|
+
return out;
|
|
1324
945
|
}
|
|
1325
|
-
function
|
|
1326
|
-
const
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
if (
|
|
1333
|
-
const codeEl = pre.querySelector("code");
|
|
1334
|
-
const src = codeEl?.textContent ?? "";
|
|
1335
|
-
const [beforeSrc = "", afterSrc = ""] = src.split(DIFF_SEPARATOR_RE);
|
|
1336
|
-
pre.innerHTML = renderDiff(beforeSrc, afterSrc, pre.dataset.language);
|
|
1337
|
-
const bar = document.createElement("div");
|
|
1338
|
-
bar.className = "rt-diff-copy-bar";
|
|
1339
|
-
const leftHalf = document.createElement("div");
|
|
1340
|
-
leftHalf.className = "rt-diff-copy-half";
|
|
1341
|
-
leftHalf.appendChild(
|
|
1342
|
-
makeCopyButton(() => beforeSrc, {
|
|
1343
|
-
className: "rt-diff-copy",
|
|
1344
|
-
label: "Copy before"
|
|
1345
|
-
})
|
|
1346
|
-
);
|
|
1347
|
-
const rightHalf = document.createElement("div");
|
|
1348
|
-
rightHalf.className = "rt-diff-copy-half";
|
|
1349
|
-
rightHalf.appendChild(
|
|
1350
|
-
makeCopyButton(() => afterSrc, {
|
|
1351
|
-
className: "rt-diff-copy",
|
|
1352
|
-
label: "Copy after"
|
|
1353
|
-
})
|
|
1354
|
-
);
|
|
1355
|
-
bar.appendChild(leftHalf);
|
|
1356
|
-
bar.appendChild(rightHalf);
|
|
1357
|
-
pre.prepend(bar);
|
|
1358
|
-
const label2 = buildCodeBlockLabel(pre);
|
|
1359
|
-
if (label2) pre.prepend(label2);
|
|
1360
|
-
return;
|
|
1361
|
-
}
|
|
1362
|
-
if (variant === "terminal") {
|
|
1363
|
-
pre.classList.add("rt-terminal");
|
|
1364
|
-
const codeEl = pre.querySelector("code");
|
|
1365
|
-
const source = codeEl?.textContent ?? "";
|
|
1366
|
-
if (codeEl) {
|
|
1367
|
-
const lang = pre.dataset.language || "sh";
|
|
1368
|
-
const lines = source.split("\n");
|
|
1369
|
-
codeEl.innerHTML = lines.map(
|
|
1370
|
-
(line) => `<span class="rt-term-line"><span class="rt-term-prompt" aria-hidden="true">$</span> ${highlightLine(line, lang) || " "}</span>`
|
|
1371
|
-
).join("");
|
|
1372
|
-
}
|
|
1373
|
-
pre.appendChild(makeCopyButton(() => source, { label: "Copy commands" }));
|
|
1374
|
-
return;
|
|
1375
|
-
}
|
|
1376
|
-
const label = buildCodeBlockLabel(pre);
|
|
1377
|
-
if (label) pre.prepend(label);
|
|
1378
|
-
pre.appendChild(
|
|
1379
|
-
makeCopyButton(
|
|
1380
|
-
() => pre.querySelector("code")?.textContent ?? pre.innerText
|
|
1381
|
-
)
|
|
1382
|
-
);
|
|
1383
|
-
highlightCodeBlock(pre);
|
|
946
|
+
function extractHeadingsFromElement(root, options = {}) {
|
|
947
|
+
const levels = options.levels ?? DEFAULT_LEVELS;
|
|
948
|
+
const slug = options.slugify ?? slugify;
|
|
949
|
+
const selector = levels.map((l) => `h${l}`).join(",");
|
|
950
|
+
const nodes = root.querySelectorAll(selector);
|
|
951
|
+
const used = /* @__PURE__ */ new Map();
|
|
952
|
+
nodes.forEach((n) => {
|
|
953
|
+
if (n.id) used.set(n.id, (used.get(n.id) ?? 0) + 1);
|
|
1384
954
|
});
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
.
|
|
1388
|
-
.
|
|
1389
|
-
.
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
.
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
.rt-codeblock__sep {
|
|
1400
|
-
color: #6b7280;
|
|
1401
|
-
font-size: 0.72rem;
|
|
1402
|
-
}
|
|
1403
|
-
.rt-codeblock__language {
|
|
1404
|
-
font-size: 0.6rem; letter-spacing: 0.08em;
|
|
1405
|
-
text-transform: lowercase;
|
|
1406
|
-
color: #9ca3af;
|
|
1407
|
-
padding: 0.05rem 0.35rem;
|
|
1408
|
-
border: 1px solid rgba(255,255,255,0.12);
|
|
1409
|
-
border-radius: 0.25rem;
|
|
1410
|
-
}
|
|
1411
|
-
.rt-codeblock__copy {
|
|
1412
|
-
position: absolute; top: 0.4rem; right: 0.4rem;
|
|
1413
|
-
display: inline-flex; align-items: center; justify-content: center;
|
|
1414
|
-
width: 1.75rem; height: 1.75rem; padding: 0;
|
|
1415
|
-
border: 1px solid rgba(255,255,255,0.08);
|
|
1416
|
-
border-radius: 0.375rem;
|
|
1417
|
-
background: rgba(255,255,255,0.04);
|
|
1418
|
-
color: #d1d5db;
|
|
1419
|
-
cursor: pointer;
|
|
1420
|
-
opacity: 0; transition: opacity 120ms ease, background 120ms ease, color 120ms ease;
|
|
1421
|
-
}
|
|
1422
|
-
.rt-codeblock:hover .rt-codeblock__copy,
|
|
1423
|
-
.rt-codeblock:focus-within .rt-codeblock__copy { opacity: 1; }
|
|
1424
|
-
.rt-codeblock__copy:hover { background: rgba(255,255,255,0.1); color: #fff; }
|
|
1425
|
-
.rt-codeblock__copy.is-copied { color: #34d399; opacity: 1; }
|
|
1426
|
-
@media (hover: none) { .rt-codeblock__copy { opacity: 1; } }
|
|
1427
|
-
.rt-quote-open, .rt-quote-close {
|
|
1428
|
-
font-family: Georgia, "Times New Roman", serif;
|
|
1429
|
-
font-size: 1.4em;
|
|
1430
|
-
line-height: 0;
|
|
1431
|
-
vertical-align: -0.15em;
|
|
1432
|
-
opacity: 0.6;
|
|
1433
|
-
user-select: none;
|
|
1434
|
-
}
|
|
1435
|
-
.rt-quote-open { margin-right: 0.15em; }
|
|
1436
|
-
.rt-quote-close { margin-left: 0.15em; }
|
|
1437
|
-
/* highlight.js theme handles .hljs-* color classes; we only override the
|
|
1438
|
-
default .hljs background so the per-block chrome (dark bg, terminal,
|
|
1439
|
-
diff red/green rows) wins. */
|
|
1440
|
-
.rt-codeblock .hljs,
|
|
1441
|
-
.rt-codeblock code.hljs { background: transparent; padding: 0; }
|
|
1442
|
-
|
|
1443
|
-
/* Terminal variant ------------------------------------------------------- */
|
|
1444
|
-
.rt-codeblock.rt-terminal,
|
|
1445
|
-
.rt-codeblock[data-variant="terminal"] {
|
|
1446
|
-
position: relative;
|
|
1447
|
-
padding-top: 2.25rem;
|
|
1448
|
-
background: #0b0b0d;
|
|
1449
|
-
border: 1px solid rgba(255,255,255,0.08);
|
|
1450
|
-
border-radius: 0.65rem;
|
|
1451
|
-
box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset,
|
|
1452
|
-
0 10px 30px -10px rgba(0,0,0,0.6);
|
|
1453
|
-
}
|
|
1454
|
-
.rt-codeblock.rt-terminal::before,
|
|
1455
|
-
.rt-codeblock[data-variant="terminal"]::before {
|
|
1456
|
-
content: "";
|
|
1457
|
-
position: absolute; top: 0; left: 0; right: 0; height: 1.75rem;
|
|
1458
|
-
background: linear-gradient(#1a1a1d, #141417);
|
|
1459
|
-
border-bottom: 1px solid rgba(255,255,255,0.06);
|
|
1460
|
-
border-radius: 0.65rem 0.65rem 0 0;
|
|
1461
|
-
}
|
|
1462
|
-
.rt-codeblock.rt-terminal::after,
|
|
1463
|
-
.rt-codeblock[data-variant="terminal"]::after {
|
|
1464
|
-
content: "";
|
|
1465
|
-
position: absolute; top: 0.55rem; left: 0.75rem;
|
|
1466
|
-
width: 0.65rem; height: 0.65rem; border-radius: 50%;
|
|
1467
|
-
background: #ff5f57;
|
|
1468
|
-
box-shadow:
|
|
1469
|
-
1.1rem 0 0 0 #febc2e,
|
|
1470
|
-
2.2rem 0 0 0 #28c840;
|
|
1471
|
-
}
|
|
1472
|
-
.rt-codeblock.rt-terminal code,
|
|
1473
|
-
.rt-codeblock[data-variant="terminal"] code {
|
|
1474
|
-
color: #d4d4d8;
|
|
1475
|
-
display: block;
|
|
1476
|
-
}
|
|
1477
|
-
.rt-term-line { display: block; white-space: pre-wrap; }
|
|
1478
|
-
.rt-term-prompt {
|
|
1479
|
-
color: #28c840;
|
|
1480
|
-
font-weight: 600;
|
|
1481
|
-
margin-right: 0.35em;
|
|
1482
|
-
user-select: none;
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
/* Diff variant ----------------------------------------------------------- */
|
|
1486
|
-
.rt-codeblock[data-variant="diff"] {
|
|
1487
|
-
padding: 0;
|
|
1488
|
-
overflow: hidden;
|
|
1489
|
-
background: #0b0b0d;
|
|
1490
|
-
border: 1px solid rgba(255,255,255,0.08);
|
|
1491
|
-
}
|
|
1492
|
-
.rt-codeblock[data-variant="diff"][data-filename]:not([data-filename=""]) {
|
|
1493
|
-
padding-top: 2rem;
|
|
1494
|
-
}
|
|
1495
|
-
.rt-diff-table {
|
|
1496
|
-
width: 100%;
|
|
1497
|
-
border-collapse: collapse;
|
|
1498
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1499
|
-
font-size: 0.78rem;
|
|
1500
|
-
line-height: 1.55;
|
|
1501
|
-
color: #e5e7eb;
|
|
1502
|
-
table-layout: fixed;
|
|
1503
|
-
}
|
|
1504
|
-
.rt-diff-table colgroup { display: none; }
|
|
1505
|
-
.rt-diff-table td {
|
|
1506
|
-
padding: 0 0.5rem;
|
|
1507
|
-
vertical-align: top;
|
|
1508
|
-
white-space: pre-wrap;
|
|
1509
|
-
word-break: break-word;
|
|
1510
|
-
}
|
|
1511
|
-
.rt-diff-table td.rt-diff-num {
|
|
1512
|
-
width: 2.25rem;
|
|
1513
|
-
text-align: right;
|
|
1514
|
-
color: rgba(156,163,175,0.55);
|
|
1515
|
-
user-select: none;
|
|
1516
|
-
background: rgba(255,255,255,0.02);
|
|
1517
|
-
}
|
|
1518
|
-
.rt-diff-table td.rt-diff-sign {
|
|
1519
|
-
width: 0.85rem;
|
|
1520
|
-
text-align: center;
|
|
1521
|
-
user-select: none;
|
|
1522
|
-
color: rgba(255,255,255,0.45);
|
|
1523
|
-
}
|
|
1524
|
-
.rt-diff-table td.rt-diff-line.rt-diff-rem {
|
|
1525
|
-
background: rgba(248,113,113,0.12);
|
|
1526
|
-
color: #fecaca;
|
|
1527
|
-
}
|
|
1528
|
-
.rt-diff-table tr:has(td.rt-diff-rem) td.rt-diff-sign:first-of-type {
|
|
1529
|
-
color: #f87171;
|
|
1530
|
-
}
|
|
1531
|
-
.rt-diff-table td.rt-diff-line.rt-diff-add {
|
|
1532
|
-
background: rgba(74,222,128,0.12);
|
|
1533
|
-
color: #bbf7d0;
|
|
1534
|
-
}
|
|
1535
|
-
.rt-diff-table tr:has(td.rt-diff-add) td.rt-diff-sign + td + td + td.rt-diff-sign {
|
|
1536
|
-
color: #4ade80;
|
|
1537
|
-
}
|
|
1538
|
-
.rt-diff-table td.rt-diff-line.rt-diff-empty {
|
|
1539
|
-
background: rgba(255,255,255,0.025);
|
|
1540
|
-
}
|
|
1541
|
-
.rt-diff-table tr td:nth-child(4) { border-left: 1px solid rgba(255,255,255,0.06); }
|
|
1542
|
-
.rt-diff-copy-bar {
|
|
1543
|
-
position: absolute; top: 0.4rem; left: 0; right: 0; z-index: 2;
|
|
1544
|
-
display: grid; grid-template-columns: 1fr 1fr;
|
|
1545
|
-
pointer-events: none;
|
|
1546
|
-
}
|
|
1547
|
-
.rt-diff-copy-half {
|
|
1548
|
-
display: flex;
|
|
1549
|
-
justify-content: flex-end;
|
|
1550
|
-
padding-right: 0.4rem;
|
|
1551
|
-
}
|
|
1552
|
-
.rt-diff-copy {
|
|
1553
|
-
pointer-events: auto;
|
|
1554
|
-
display: inline-flex; align-items: center; justify-content: center;
|
|
1555
|
-
width: 1.6rem; height: 1.6rem; padding: 0;
|
|
1556
|
-
border: 1px solid rgba(255,255,255,0.1);
|
|
1557
|
-
border-radius: 0.375rem;
|
|
1558
|
-
background: rgba(20,20,23,0.85);
|
|
1559
|
-
color: #d1d5db;
|
|
1560
|
-
cursor: pointer;
|
|
1561
|
-
opacity: 0; transition: opacity 120ms ease, background 120ms ease, color 120ms ease;
|
|
1562
|
-
}
|
|
1563
|
-
.rt-codeblock[data-variant="diff"]:hover .rt-diff-copy,
|
|
1564
|
-
.rt-codeblock[data-variant="diff"]:focus-within .rt-diff-copy { opacity: 1; }
|
|
1565
|
-
.rt-diff-copy:hover { background: rgba(0,0,0,0.92); color: #fff; }
|
|
1566
|
-
.rt-diff-copy.is-copied { color: #34d399; opacity: 1; }
|
|
1567
|
-
@media (hover: none) { .rt-diff-copy { opacity: 1; } }
|
|
1568
|
-
`;
|
|
1569
|
-
var styleInjected = false;
|
|
1570
|
-
function ensureCodeBlockStyles() {
|
|
1571
|
-
if (styleInjected || typeof document === "undefined") return;
|
|
1572
|
-
if (document.getElementById("rt-codeblock-style")) {
|
|
1573
|
-
styleInjected = true;
|
|
1574
|
-
return;
|
|
1575
|
-
}
|
|
1576
|
-
const tag = document.createElement("style");
|
|
1577
|
-
tag.id = "rt-codeblock-style";
|
|
1578
|
-
tag.textContent = CODEBLOCK_STYLE;
|
|
1579
|
-
document.head.appendChild(tag);
|
|
1580
|
-
styleInjected = true;
|
|
1581
|
-
}
|
|
1582
|
-
function RichTextContent({
|
|
1583
|
-
html,
|
|
1584
|
-
classMap,
|
|
1585
|
-
as = "div",
|
|
1586
|
-
className
|
|
1587
|
-
}) {
|
|
1588
|
-
const merged = react.useMemo(
|
|
1589
|
-
() => mergeClassMap(DEFAULT_CLASS_MAP, classMap),
|
|
1590
|
-
[classMap]
|
|
1591
|
-
);
|
|
1592
|
-
const safe = react.useMemo(
|
|
1593
|
-
() => parseRichText(html, { classMap: merged }),
|
|
1594
|
-
[html, merged]
|
|
1595
|
-
);
|
|
1596
|
-
const ref = react.useRef(null);
|
|
1597
|
-
react.useEffect(() => {
|
|
1598
|
-
ensureCodeBlockStyles();
|
|
1599
|
-
if (ref.current) {
|
|
1600
|
-
enhanceCodeBlocks(ref.current);
|
|
1601
|
-
enhanceBlockquotes(ref.current);
|
|
1602
|
-
}
|
|
1603
|
-
}, [safe]);
|
|
1604
|
-
return react.createElement(as, {
|
|
1605
|
-
ref,
|
|
1606
|
-
className,
|
|
1607
|
-
dangerouslySetInnerHTML: { __html: safe }
|
|
955
|
+
const out = [];
|
|
956
|
+
let i = 0;
|
|
957
|
+
nodes.forEach((node) => {
|
|
958
|
+
const level = Number(node.tagName.slice(1));
|
|
959
|
+
const text = (node.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
960
|
+
if (!text) return;
|
|
961
|
+
if (!node.id) {
|
|
962
|
+
node.id = uniqueId(slug(text, i), used);
|
|
963
|
+
}
|
|
964
|
+
if (options.scrollMarginTop != null) {
|
|
965
|
+
node.style.scrollMarginTop = `${options.scrollMarginTop}px`;
|
|
966
|
+
}
|
|
967
|
+
out.push({ id: node.id, text, level });
|
|
968
|
+
i++;
|
|
1608
969
|
});
|
|
970
|
+
return out;
|
|
1609
971
|
}
|
|
1610
972
|
|
|
1611
|
-
exports.AsteroidCMSProvider = AsteroidCMSProvider;
|
|
1612
|
-
exports.RichTextContent = RichTextContent;
|
|
1613
973
|
exports.buildCmsQuery = buildCmsQuery;
|
|
1614
974
|
exports.cmsImage = cmsImage;
|
|
1615
975
|
exports.createApolloClient = createApolloClient;
|
|
976
|
+
exports.extractHeadingsFromElement = extractHeadingsFromElement;
|
|
977
|
+
exports.extractHeadingsFromHtml = extractHeadingsFromHtml;
|
|
1616
978
|
exports.fetchCmsContent = fetchCmsContent;
|
|
1617
979
|
exports.getContentReadTime = getContentReadTime;
|
|
1618
980
|
exports.parseRichText = parseRichText;
|
|
1619
981
|
exports.removeEmptyParagraphs = removeEmptyParagraphs;
|
|
1620
|
-
exports.
|
|
1621
|
-
exports.useCmsContent = useCmsContent;
|
|
1622
|
-
exports.useCmsImage = useCmsImage;
|
|
1623
|
-
exports.useCmsMutate = useCmsMutate;
|
|
982
|
+
exports.slugify = slugify;
|
|
1624
983
|
//# sourceMappingURL=index.cjs.map
|
|
1625
984
|
//# sourceMappingURL=index.cjs.map
|