### Example directory structure for a Mattrbld project
Source: https://mattrbld.com/docs/quickstart
Mattrbld doesn't enforce a specific directory structure; this is a simple structure used in the guide for clarity. Only the 'content' folder and its 'posts' and 'uploads' subfolders are relevant; other folders are for context.
```text
my-first-mattrbld-project
├── .git
├── content
│ ├── posts
│ └── uploads
├── src
│ └── (files and folders relating to your SSG)
└── (other files)
```
--------------------------------
### Blog post example: file and Schema
Source: https://mattrbld.com/docs/fields-and-types
Illustrates a blog post file with frontmatter and Markdown body, and the corresponding Schema that maps fields to that structure. The 'content' key is special and writes to the body instead of frontmatter.
```markdown
---
title: Hello World
published: 2021-06-23
---
# Hello World!
This is the **Markdown** content of the post.
```
```json
{
"fields": [
{
"type": "text",
"key": "title"
},
{
"type": "date",
"key": "published"
},
{
"type": "rich text",
"key": "content",
"options": {
"outputFormat": "markdown"
}
}
]
}
```
--------------------------------
### Localised field output example
Source: https://mattrbld.com/docs/multi-language-content
Shows the JSON output of the same field when localisation is enabled, with language codes 'de-DE' and 'en-GB'.
```json
{
"title": {
"de-DE": "wert",
"en-GB": "value"
}
}
```
--------------------------------
### Localised to non-localised property conversion example
Source: https://mattrbld.com/docs/multi-language-content
Shows the JSON structure before and after converting a localised property back to non-localised. The value of the first locale (de-DE) is kept and other values are discarded.
```json
{
"title": {
"de-DE": "wert",
"en-GB": "value"
}
}
```
```json
{
"title": "wert"
}
```
--------------------------------
### Non-localised to localised property conversion example
Source: https://mattrbld.com/docs/multi-language-content
Shows the JSON structure before and after converting a non-localised property to a localised one. The current value becomes the value of the first locale (de-DE) and other locales are set to null.
```json
{
"title": "value"
}
```
```json
{
"title": {
"de-DE": "value",
"en-GB": null
}
}
```
--------------------------------
### showByValue visibility option examples
Source: https://mattrbld.com/docs/fields-and-types
Two JSON examples of the showByValue visibility option. The first shows a field when the value of field "test" matches the regular expression /[0-9]+/. The second shows a field when the value of field "toggle" is false. Both use the comparator and field keys; the first uses the string value "matches" to trigger regex matching, the second uses a boolean value for strict equality.
```json
{
"comparator": "/[0-9]+/",
"field": "test",
"value": "matches"
}
```
```json
{
"comparator": null,
"field": "toggle",
"value": false
}
```
--------------------------------
### Handling messages from optional features
Source: https://mattrbld.com/docs/implementing-the-preview-protocol
Example of handling messages inside the handleMessage() function. It checks for handshake, then for the 'feature' property to handle optional features like comments, and finally renders content for regular updates.
```javascript
// inside the handleMessage() function
if (data && data.handshake) {
// handle handshake
} else if (data && data.feature === 'comments') {
// handle comment-feature related messages e.g.:
switch (data.type) {
case 'modechange':
// handle modechange event
break;
default:
break;
}
} else if (data) {
// render content
}
```
--------------------------------
### Non-localised field output example
Source: https://mattrbld.com/docs/multi-language-content
Shows the JSON output of an Unformatted Text field with key 'title' when localisation is not enabled.
```json
{
"title": "value"
}
```
--------------------------------
### handlePageClick function for comment mode
Source: https://mattrbld.com/docs/preview-features/comments
Example function to handle clicks on the preview page, distinguishing between comment markers and the page itself. Sends a message to Mattrbld with the appropriate event type ('commentClick' or 'pageClick') and coordinates.
```javascript
function handlePageClick(e) {
const {
target, clientX, clientY, pageX, pageY,
} = e;
const isCommentMarker =
target.classList.contains('comment-marker');
const data = {
feature: 'comments',
payload: {
clientX,
clientY,
},
};
if (isCommentMarker) {
data.type = 'commentClick';
data.payload.id = target.dataset.id;
} else {
data.type = 'pageClick';
data.payload.pageX = pageX;
data.payload.pageY = pageY;
}
cachedSource.postMessage(
data, MATTRBLD_INSTANCE
);
}
```
--------------------------------
### Handling addCommentMarker event in postMessage handler
Source: https://mattrbld.com/docs/preview-features/comments
Example switch-case handler for the 'addCommentMarker' event, which calls a function to create a comment marker on the preview page.
```javascript
// in the switch-case within the postMessage
// handler
case 'addCommentMarker':
const { comment } = data.payload;
// a function that places comment markers on
// the preview page
createCommentMarker(comment);
break;
```
--------------------------------
### JavaScript function to get localised value
Source: https://mattrbld.com/docs/multi-language-content
A simple JavaScript function that retrieves a localised value from content using a locale key. Includes example content and usage calls. Note the typo 'getLocalisedValule' in the calls.
```javascript
// Simple example for brevity
function getLocalisedValue(content, key, locale) {
if (content[key]) return content[key][locale];
else throw new Error(`${key} could not be found in content`);
}
const content = {
title: {
'de-DE': 'wert',
'en-GB': 'value',
},
};
getLocalisedValule(content, 'title', 'en-GB'); // → 'value'
getLocalisedValule(content, 'title', 'de-DE'); // → 'wert'
```
--------------------------------
### Handling deleteCommentMarker event in postMessage handler
Source: https://mattrbld.com/docs/preview-features/comments
Example switch-case handler for the 'deleteCommentMarker' event, which calls a function to remove a comment marker from the preview page.
```javascript
// in the switch-case within the postMessage
// handler
case 'deleteCommentMarker':
const { comment } = data.payload;
// a function that removes comment markers from
// the preview page
deleteCommentMarker(comment);
break;
```
--------------------------------
### Complete Preview Protocol Implementation
Source: https://mattrbld.com/docs/implementing-the-preview-protocol
A complete HTML page that implements the Mattrbld preview protocol. It listens for messages, verifies the origin against the Mattrbld instance URL, handles the handshake by responding with the handshake ID and an empty supports array, and renders received data as JSON. It also signals Mattrbld that the page has loaded when opened in a separate tab or window.
```html
Preview Protocol Tester
Mattrbld Preview Protocol Tester
Ready for output.
```
--------------------------------
### Legacy config.json sidebar entries for system types
Source: https://mattrbld.com/docs/sidebar
For projects configured before v1.0.0, add the Dashboard, Media Library, and Settings entries manually to the project's config.json file in the .mattrbld directory. The entries use the 'protected' flag and 'onlyPrivileged' for Settings.
```json
"sidebar": [
{
"label": "Dashboard",
"icon": "grid",
"target": {
"name": "Project"
},
"protected": true
},
{
"label": "Media Library",
"icon": "image-stack",
"target": {
"name": "Project.MediaLibrary"
},
"protected": true
},
{
"label": "Settings",
"icon": "settings",
"target": {
"name": "Project.Settings"
},
"onlyPrivileged": true,
"protected": true
}
]
```
--------------------------------
### Enable Umami analytics with .env.production.local
Source: https://mattrbld.com/docs/self-hosting-overview
Add this .env.production.local file to the root directory to enable Umami analytics. The VUE_APP_UMAMI_SCRIPT variable must be set to the full URL of the Umami script (e.g., https://analytics.example.com/umami.js), and VUE_APP_UMAMI_ID to the ID Umami assigned when you added your Mattrbld domain as a site. If these variables are absent when npm run build is run, no analytics are reported.
```plaintext
VUE_APP_UMAMI_SCRIPT=[URL of your Umami script]
VUE_APP_UMAMI_ID=[ID of your instance in Umami]
```
--------------------------------
### Filesystem structure tree
Source: https://mattrbld.com/docs/how-it-works
Shows the virtual file system layout Mattrbld generates in IndexedDB. The root contains a projects directory for each imported project, a users directory for local user profiles, and a mattrbld.config file for global configuration.
```text
root
├── projects
│ ├── your-project
│ │ ├── .mattrbld
│ │ └── (all other files in your repository)
│ └── (repeat for every other imported project)
├── users
│ ├── user-at-example-com.json
│ ├── user-at-example-com.jpg
│ └── (repeat for every other local user)
└── mattrbld.config
```
--------------------------------
### Handle binary image data and replace content src with ObjectURL
Source: https://mattrbld.com/docs/preview-limitations
Processes preview data by storing binary media blobs in a Map as ObjectURLs, then recursively replaces src attributes in content with those URLs. Media files are unprocessed and may not be resized or optimized.
```javascript
const images = new Map();
// data is the data passed by the Mattrbld preview
function handlePreviewData(data) {
if (data.imageMap && data.imageMap.size > 0) {
data.imageMap.forEach((image, path) => {
if (images.has(path)) return;
images.set(
path,
URL.createObjectURL(image)
);
});
}
// the actual content is in data.data
content = replaceContent(data.data);
}
// A recursive function to replace content
// with usable data, could also be used to convert
// Markdown and other things
function replaceContent(obj) {
if (!obj || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) {
return obj.map((item) => {
return replaceContent(item));
}
}
return Object.entries(obj)
.reduce((content, [key, value]) => {
if (
typeof value === 'string'
&& key === 'src'
&& images.has(value)
) content[key] = images.get(value);
else content[key] = replaceContent(value);
return content;
}, {});
}
```
--------------------------------
### Mattrbld Invite Link URL Structure
Source: https://mattrbld.com/docs/users
Invite link structure for Mattrbld. Only the repo and branch parameters are required; all others are optional and fall back to defaults or prompt the user. The email parameter, if present, is displayed read-only when the link is opened.
```text
// Formatted and URL encoding omitted
// for better legibility
https://app.mattrbld.com/import
?name=Example User
&email=user@example.com
&repo=https://git-provider.com/path/to/repo.git
&branch=main
&proxy=https://proxy-url.com
&inviter=Name of person inviting
&projectName=Name of the project
```
--------------------------------
### Content-Security-Policy header for frame-ancestors
Source: https://mattrbld.com/docs/common-issues-with-realtime-previews
Set the Content-Security-Policy header to allow the preview page to be opened in an iFrame on the Mattrbld origin. The X-Frame-Options header is obsolete and should be replaced with this CSP header.
```http
Content-Security-Policy: frame-ancestors 'self' https://app.mattrbld.com
```
--------------------------------
### Handle initialData event
Source: https://mattrbld.com/docs/preview-features/comments
Handle the initialData event by creating comment markers for each comment. Ensure positions are handled appropriately.
```javascript
// in the switch-case within the postMessage
// handler
case 'initialData':
const { comments } = data.payload;
comments.forEach((comment) => {
// a function that places comment markers on
// the preview page
createCommentMarker(comment)
});
break;
```
--------------------------------
### Preview page handshake handler
Source: https://mattrbld.com/docs/implementing-the-preview-protocol
JavaScript code for the preview page to handle the handshake with Mattrbld. It validates the origin, caches the message source, and responds with the handshake code and an empty supports array. Also signals Mattrbld that the page has loaded when opened in a separate tab or window.
```javascript
// replace this with the URL of your Mattrbld instance if you're using a custom one!
const MATTRBLD_INSTANCE = "https://app.mattrbld.com";
const cachedSource = null;
function handleMessage(e) {
// get the relevant details from the message
const { origin, data, source } = e;
if (origin !== MATTRBLD_INSTANCE) {
// handle postMessages from other origins
// usually by throwing an error
return;
}
// cache the message source to send data back
cachedSource = source;
// handle the handshake
if (data && data.handshake) {
cachedSource.postMessage(
{
handshake: data.handshake,
// include optional features here
// but only if implemented!
supports: [],
},
MATTRBLD_INSTANCE
);
}
}
// handle messages coming from Mattrbld
window.addEventListener('message', handleMessage);
// needed to signal Mattrbld that the page has loaded if it was opened in a separate tab / window
if (window.opener) {
window.opener.postMessage(
{ loaded: true }, MATTRBLD_INSTANCE
);
}
```
--------------------------------
### Preview data format
Source: https://mattrbld.com/docs/implementing-the-preview-protocol
The general preview data sent after handshake and after every content change. The 'url' property is compiled based on collection URL-template, localization, and draft status; otherwise it is assembled from the project path. The 'data' property contains content according to the Schema.
```javascript
{
// the name of the collection the content is in
collection: 'Pages.json',
// the URL of the content item
url: '/docs/implementing-the-preview-protocol/',
// the content according to the Schema
data: {…},
// any Media Library images
imageMap: