Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bump-tar-7-5-21.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'create-tina-app': patch
---

Bump `tar` to 7.5.21
7 changes: 7 additions & 0 deletions .changeset/common-eyes-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"tinacms": patch
"@tinacms/app": patch
"@tinacms/schema-tools": patch
---

feat: when WorkOS is enabled, use a redirect-based workflow for authentication
9 changes: 9 additions & 0 deletions .changeset/great-buses-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"tinacms": patch
---

Fix the preview iframe going unresponsive after resizing the sidebar.

Dragging the resize handle used to disable pointer events on the entire app so the drag would survive the cursor crossing into the preview. Releasing the drag over the preview left it dead to clicks and scrolling until you clicked the sidebar again. The handle now uses pointer capture, which keeps the drag targeting the handle without touching the rest of the page, so the preview stays interactive throughout.

Also corrects the handle's fullscreen guard, which read a `fullscreen` key the sidebar context has never provided and so never fired. No behaviour change today, since nothing currently puts the sidebar into the fullscreen display state.
9 changes: 9 additions & 0 deletions .changeset/mdx-mark-edge-whitespace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@tinacms/mdx": patch
---

Bold, italic and strikethrough now survive a leading or trailing space in the selection. Selecting `word ` and applying bold used to save `**word **`. CommonMark cannot close emphasis that sits against a space, so the published page showed literal asterisks and the formatting was lost, even though the editor still looked right. The space now sits outside the markers, giving `a **word** more`.

Indentation at the start of a line is kept as well. A bare space there is whitespace a Markdown parser may discard, and four of them open an indented code block, so an indented line used to reload without its spaces on the `mdx` parser and as a code block on the `markdown` parser. This applies to the first line of a paragraph and to a line broken with Shift+Enter. The leading space is now written as ` `, so the text comes back the way it was left.

The fix also covers marks holding only whitespace, empty marks, marks spanning several text nodes, marks inside a link, and combined bold and italic. Whitespace inside a mark, as in `**Hello *world*, again**`, still round trips unchanged.
9 changes: 9 additions & 0 deletions .changeset/ninety-deserts-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"tinacms": minor
"@tinacms/cli": minor
"@tinacms/schema-tools": minor
"@tinacms/web-components": minor
---

feat: add tina-markdown web component
feat: add visual-editing library for web components
7 changes: 7 additions & 0 deletions .changeset/sanitise-tina-markdown-raw-html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@tinacms/web-components": minor
---

Sanitise `html` / `html_inline` nodes in `tina-markdown` and add a
`TinaMarkdown.components` map for per-node-type renderers, matching the
`components` prop on the React and Astro renderers.
6 changes: 6 additions & 0 deletions .changeset/spicy-donuts-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@tinacms/cli': patch
'@tinacms/scripts': patch
---

Bump `esbuild` to 0.28.1, picking up upstream security fixes (GHSA-g7r4-m6w7-qqqr, GHSA-gv7w-rqvm-qjhr)
1 change: 1 addition & 0 deletions examples/web-components/kitchen-sink/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
2 changes: 2 additions & 0 deletions examples/web-components/kitchen-sink/admin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
index.html
assets/
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: My first blog
---

Woah! what do you know. This is my *first blog post*.

Just think of all the **things** I could write…

~~I wonder if…~~
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: 'Reject frameworks, accept JS'
---

You read the title, JS is the **goat**.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: Supported Markdown
---

# Heading 1

## Heading 2

### Heading 3

#### Heading 4

##### Heading 5

###### Heading 6

This is a paragraph!

1. ordered list item 1
2. ordered list item 2

* unordered list item 1
* unordered list item 2

> This is a block quote!

![](/example-image.jpg)

[This is a block link](https://tina.io)

This is an [inline link](https://tina.io) !!

```javascript
function helloWorld(name) {
console.log("hello", name)
}
```

***

this is

a manual

line break

| You | Me | Us |
| ----- | -------- | --------------------- |
| Okay | Handsome | A match |
| Funny | Dumb | Yet another match \<3 |

<div id="raw-html-test">
<center>
<p>This is raw html</p>
</center>
</div>

this is `inline` code block

<PostPreview title="⭐ custom components ⭐">
This is a custom component!
</PostPreview>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
166 changes: 166 additions & 0 deletions examples/web-components/kitchen-sink/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="/style.css">
<script src="post-preview.js"></script>
<!--
The generated client imports `tinacms/dist/client`, a bare specifier
the browser can't resolve. Map it to the installed package's entry file.
-->
<script type="importmap">
{
"imports": {
"tinacms/dist/client": "./node_modules/tinacms/dist/client.js"
}
}
</script>
</head>

<body>
<h1>My Tina site</h1>

<div id="posts-container">
<h2>Blog posts</h2>
<ul id="posts">
</ul>
</div>

<script type="module">
// Pull in the generated client.
// Emitted by `tinacms dev` / `tinacms build` into `tina/__generated__`
import {client} from "./tina/__generated__/client.js";

// Register the `tina-markdown` component.
import {TinaMarkdown} from "./node_modules/@tinacms/web-components/dist/tina-markdown.js";

// Register custom component for rich-text consumption.
TinaMarkdown.components["PostPreview"] = (node) => {
const el = document.createElement("post-preview");

const title = document.createElement("span");
title.slot = "title";
title.textContent = node.props.title ?? "";

const tinaMarkdown = document.createElement("tina-markdown");
const markdownAst = JSON.stringify(node.props.children ?? {});
tinaMarkdown.setAttribute("content", markdownAst);

const content = document.createElement("span");
content.slot = "content";
content.appendChild(tinaMarkdown);

el.append(title, content);

return el;
};

// Imports required for visual editing.
// - `createTina` fetches a provided connection and renders the content
// when changes are detected.
// - `tinaField` generates unique IDs used by Tina internally for
// click-to-edit functionality.
import {createTina, tinaField} from "./node_modules/@tinacms/web-components/dist/visual-editing.js";

const postsContainer = document.getElementById("posts");

// Build the `#posts` list from a query result. `data` is stamped with
// `_content_source` metadata by `createTina`, which `tinaField()`
// reads to produce the `data-tina-field` markers the admin uses for
// click-to-edit.
//
// Re-invoked on every content update.
function renderPosts(data) {
const posts = data.postConnection.edges.map((edge) => edge.node);

postsContainer.replaceChildren();
for (const post of posts) {
// Create a `post-preview` element.
// This is a web component (./post-preview.js) with two slots:
// - title (the blog post title)
// - content (the markdown content)
//
// Read more: https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots
const postPreview = document.createElement("post-preview");

// Create a "title" slot and set its content to the post's title.
//
// The following JS is equivalent to:
// <span data-tina-field="$bodyField" slot="title">$post.title</span>
const title = document.createElement("span");
title.slot = "title";
title.textContent = post.title;
// Generate a unique id and set it to the elements `data-tina-field`.
// This enables click-to-edit in the admin.
const titleField = tinaField(post, "title");
if (titleField) title.setAttribute("data-tina-field", titleField);

// Create a `tina-markdown` element.
// This is a web component with an attribute `content`.
// `content` is the stringified AST provided by a tina `rich-text` element.
//
// The following JS is equivalent to:
// <tina-markdown data-tina-field="$bodyField" content="$markdownAst"></tina-markdown>
const tinaMarkdown = document.createElement("tina-markdown");
const markdownAst = JSON.stringify(post.body);
tinaMarkdown.setAttribute("content", markdownAst)
// Generate a unique id and set it to the elements `data-tina-field`.
// This enables click-to-edit in the admin.
const bodyField = tinaField(post, "body");
if (bodyField) tinaMarkdown.setAttribute("data-tina-field", bodyField);

// Web components exist in a shadowdom and do not inherit styling.
// To style it, you have to embed the stylings manually.
//
// Read more: https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#encapsulation_from_css
const styles = document.createElement("style");
styles.appendChild(document.createTextNode(`@import "./style.css";`));
tinaMarkdown.shadowRoot.appendChild(styles);

// Create a "content" slot and append the `tina-markdown` component.
//
// The following JS is equivalent to:
// <span slot="content">
// <tina-markdown data-tina-field="$bodyField" content="$markdownAst"></tina-markdown>
// </span>
const content = document.createElement("span");
content.slot = "content";
content.appendChild(tinaMarkdown);

// Append the slots to the `post-preview` element.
postPreview.append(title, content);

// #posts is a `ul` so `post-preview` should be wrapped in an `li`.
//
// The following JS is equivalent to:
// <li style="margin: 1rem 0;">
// <post-preview>
// <span data-tina-field="$bodyField" slot="title">$post.title</span>
// <span slot="content">
// <tina-markdown data-tina-field="$bodyField" content="$markdownAst"></tina-markdown>
// </span>
// </post-preview>
// </li>
const li = document.createElement("li");
li.setAttribute("style", "margin: 1rem 0;");
li.appendChild(postPreview);

// Append the `li` to the `ul`.
postsContainer.appendChild(li);
}
};

// Fetch the posts and enable visual editing.
//
// `createTina` fetches via `query`, renders via `renderPosts`, and
// re-renders whenever content is edited in the admin.
createTina({
query: () => client.queries.postConnection(),
render: renderPosts,
}).init();
</script>
</body>

</html>
16 changes: 16 additions & 0 deletions examples/web-components/kitchen-sink/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@examples/web-components-kitchen-sink",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "tinacms dev -c \"python3 -m http.server\"",
"build": "tinacms build"
},
"dependencies": {
"@tinacms/web-components": "workspace:^"
},
"devDependencies": {
"@tinacms/cli": "workspace:*",
"tinacms": "workspace:*"
}
}
36 changes: 36 additions & 0 deletions examples/web-components/kitchen-sink/post-preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const postPreviewTemplate = document.createElement('template');
postPreviewTemplate.id = 'post-preview';
postPreviewTemplate.innerHTML = `
<style>
.post-card {
border: 1px solid #B8B8B8;
border-radius: 10px;

filter: drop-shadow(-6px 4px 5px #00000010);
background-color: white;

padding: 0.5rem 1rem;
}
</style>

<div class="post-card">
<h1>
<slot name="title"></slot>
</h1>
<p class="-content">
<slot name="content"></slot>
</p>
</div>
</div>
`;

class PostPreviewComponent extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'closed' });

shadowRoot.appendChild(postPreviewTemplate.content.cloneNode(true));
}
}

customElements.define('post-preview', PostPreviewComponent);
Loading
Loading