The script generates and appends a Table of Contents (TOC) for a rich text field (Description). It works by fetching the text, identifying headings and GUID anchor links to construct a TOC, and then updating the text with this TOC added at the beginning.
In the gif I show two button automations, with and without ‘Callout’.
Explanation
Note: I chose to use JSON fetching and saving because directly manipulating the document content via JSON ensures that the existing GUIDs in the headers remain unchanged.
Step 1: Initialization and Current Entity Details Retrieval
- The script starts by accessing the Fibery API to and identifies the database of interest (
pageType
) and establishes the base URL for constructing anchor links. - It retrieves details of the current entity, focusing on the
Description
field with its unique Document ID and secret. ThePublic Id
of the current entity is also retrieved, which is crucial for constructing anchor links within the TOC.
Step 2: Generating the Table of Contents (TOC)
- Utilizing the Document ID and secret of the
Description
field, the script fetches the current content of the Document entity associated with the current entity’s rich text field. - It then proceeds to generate the TOC by analyzing the headings within the document content, calling upon
createHierarchicalTOCJson
.
Step 3: TOC Construction
- The TOC is initiated with a “Table of Contents” title in bold, followed by a line break to separate it from subsequent entries.
- As the script iterates through each block of the document content, it identifies headings and creates corresponding TOC entries. Each entry:
- Is prefixed with a bullet point, where indentation is applied based on the heading level to mirror the document’s structure hierarchically. This indentation is calculated by adding spaces, adjusting for the heading’s level.
- Includes a clickable link composed of the heading’s text and an anchor link crafted using the
Public Id
of the current entity and the heading’s unique identifier (guid
). - Is separated by line breaks (
hard_break
), ensuring each TOC entry appears on a new line, maintaining readability.
Step 4: Document Update with TOC
- After assembling the TOC, the script prepares a new JSON structure for the document, combining the TOC (assembled as a cohesive paragraph with entries and breaks) with the original content.
- This new document content is then saved back to the
Description
rich text field of the current entity, updating it with the TOC at the forefront, using the dedicated Document ID and secret for the update operation.
Script
const fibery = context.getService('fibery');
const pageType = 'YourSpace/DataBaseName'; // Replace with your actual database name
const baseUrl = 'https://youraccount.fibery.io/YourSpace/DatabaseName/'; // Your Fibery instance base URL
async function generateTOCAndAppend() {
try {
const currentPageEntity = args.currentEntities[0];
const pageEntity = await fibery.getEntityById(pageType, currentPageEntity.id, ['Description', 'Public Id']);
if (pageEntity.Description && pageEntity.Description.Secret) {
let descriptionContentJson = await fibery.getDocumentContent(pageEntity.Description.Secret, 'json');
const tocContentJson = createHierarchicalTOCJson(descriptionContentJson, pageEntity['Public Id']);
let updatedDescriptionContentJson = {
...descriptionContentJson,
doc: {
...descriptionContentJson.doc,
content: [...tocContentJson, ...descriptionContentJson.doc.content]
}
};
await fibery.setDocumentContent(pageEntity.Description.Secret, JSON.stringify(updatedDescriptionContentJson), 'json');
}
} catch (error) {
console.error('Error in script:', error);
}
}
function createHierarchicalTOCJson(descriptionContentJson, publicId) {
const tocEntries = [{
type: "paragraph",
content: [{
type: "text",
text: "Table of Contents",
marks: [{ type: "strong" }]
}, {
type: "hard_break"
}]
}];
let tocContentArray = tocEntries[0].content; // Use the content array of the first (and only) paragraph.
descriptionContentJson.doc.content.forEach((block, index, array) => {
if (block.type === 'heading' && block.attrs && block.content) {
const level = block.attrs.level;
const textContent = block.content.map(element => element.text || '').join('');
const anchorLink = `${baseUrl}${publicId}/anchor=${block.attrs.guid}`;
// Adjusting to always include a bullet point
let prefix = " ".repeat((level - 1) * 4);
let bulletPoint = "• ";
let prefixContent = `${prefix}${bulletPoint}`;
// Add a hard break before every entry except the first
if (tocContentArray.length > 2) { // Accounts for title and initial break
tocContentArray.push({
type: "hard_break"
});
}
// Add prefix content (bullet point)
tocContentArray.push({
type: 'text',
text: prefixContent
});
// Add the clickable text content
tocContentArray.push({
type: 'text',
text: textContent,
marks: [{
type: 'link',
attrs: { href: anchorLink }
}]
});
}
});
return tocEntries;
}
await generateTOCAndAppend();