Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
783b497
Add @tiptap/extension-mention and @tiptap/suggestion deps
jasonvarga Jul 22, 2026
0472997
Add shared Autocomplete mention extension and SuggestionList
jasonvarga Jul 22, 2026
4da0bb0
Add AutocompleteEditor primitive component and generic toolbar
jasonvarga Jul 22, 2026
81ef05a
Implement AutocompleteEditor inline and line-break modes
jasonvarga Jul 22, 2026
b68f9d1
Add Storybook story for AutocompleteEditor primitive
jasonvarga Jul 22, 2026
cea8b23
Add AutocompleteEditor fieldtype
jasonvarga Jul 22, 2026
4e021ad
Align @tiptap/* packages to a single compatible version
jasonvarga Jul 22, 2026
61f92d4
Export AutocompleteEditor from @statamic/cms ui module
jasonvarga Jul 22, 2026
91c4db3
Add PHPUnit tests for AutocompleteEditor fieldtype
jasonvarga Jul 22, 2026
fa0a569
Add Vitest tests for AutocompleteEditor primitive
jasonvarga Jul 22, 2026
8f06a54
Register AutocompleteEditor fieldtype
jasonvarga Jul 22, 2026
2032a59
Fix AutocompleteEditor UI to match Bard treatment
jasonvarga Jul 22, 2026
6480d01
Render mention as a Badge instead of plain text
jasonvarga Jul 22, 2026
8fe2f18
Fix AutocompleteEditor mention badge alignment and toolbar focus ring
jasonvarga Jul 22, 2026
fc4e21c
Merge branch '6.x' into autocomplete-editor
jasonvarga Aug 6, 2026
315be8d
Stop storing mention labels
jasonvarga Aug 7, 2026
5365033
Add markdown conversion for AutocompleteEditor content
jasonvarga Aug 7, 2026
a433b9d
Store markdown instead of ProseMirror in AutocompleteEditor fieldtype
jasonvarga Aug 7, 2026
79ff942
Simplify AutocompleteEditor fieldtype to store a plain string
jasonvarga Aug 7, 2026
f7eed84
Prevent the mention sentinel leaking into stored content
jasonvarga Aug 7, 2026
b358867
Move markdown serialization off the shared mention extension
jasonvarga Aug 7, 2026
4d2d895
Guard against non-string values in markdownToContent
jasonvarga Aug 7, 2026
4c31801
Match AutocompleteEditor content typography to Bard
jasonvarga Aug 7, 2026
b4f0c5c
Match AutocompleteEditor padding to Bard
jasonvarga Aug 7, 2026
d689dd2
Fix AutocompleteEditor spacing in block and inline modes
jasonvarga Aug 7, 2026
18420a4
Use square brackets for AutocompleteEditor mention tokens
jasonvarga Aug 11, 2026
cf1245a
Add a Mentions class for resolving AutocompleteEditor tokens
jasonvarga Aug 11, 2026
1dfdbda
Augment AutocompleteEditor content as markdown
jasonvarga Aug 11, 2026
dae6db1
Merge remote-tracking branch 'origin/6.x' into autocomplete-editor
jasonvarga Aug 11, 2026
2aa2b50
Merge remote-tracking branch 'origin/6.x' into autocomplete-editor
jasonvarga Aug 11, 2026
aedfd66
Show mentions whose option no longer exists
jasonvarga Aug 21, 2026
0538278
Merge remote-tracking branch 'origin/6.x' into autocomplete-editor
jasonvarga Oct 13, 2026
22f6ad7
Escape block markers when serializing to markdown
jasonvarga Oct 13, 2026
ffa5a8b
Keep escaped literal mentions out of augmented HTML
jasonvarga Oct 13, 2026
946cddd
Restrict mention values to the token grammar
jasonvarga Oct 13, 2026
bc3ee21
Add a plain text mode to the autocomplete editor
jasonvarga Oct 13, 2026
4744f2a
Let mentions be resolved around a template pass
jasonvarga Oct 13, 2026
8545e84
Remove the Allow Antlers toggle from the autocomplete editor
jasonvarga Oct 13, 2026
1b83df8
Make parseMarkdown() static
jasonvarga Oct 13, 2026
10ba94d
Add AutocompleteEditor::renderHtml()
jasonvarga Oct 13, 2026
fcbd380
Only resolve mentions in text
jasonvarga Oct 14, 2026
23c5a67
Limit autocomplete editor values and avoid backtracking on backslash …
jasonvarga Oct 14, 2026
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
8 changes: 8 additions & 0 deletions lang/en/fieldtypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
'assets.dynamic_folder_pending_save' => 'This field will be available after saving.',
'assets.title' => 'Assets',
'asset_folders.config.container' => 'Choose which asset container to use for this field.',
'autocomplete_editor.config.buttons' => 'Choose which buttons to show in the toolbar.',
'autocomplete_editor.config.enable_line_breaks' => 'Allows adding line breaks to the content.',
'autocomplete_editor.config.inline' => 'Disable all block-level elements like headings and lists.',
'autocomplete_editor.config.markdown' => 'Allow formatting, stored as Markdown. When disabled, the content is plain text and mentions.',
'autocomplete_editor.config.options' => 'Set the values and their optional labels available for autocompletion.',
'autocomplete_editor.config.placeholder' => 'Text to display when the field is empty.',
'autocomplete_editor.config.trigger' => 'The character that triggers the autocomplete suggestions.',
'autocomplete_editor.title' => 'Autocomplete Editor',
'bard.config.allow_source' => 'Enable to view the HTML source code while writing.',
'bard.config.always_show_set_button' => 'Enable to always show the "Add Set" button.',
'bard.config.buttons' => 'Choose which buttons to show in the toolbar.',
Expand Down
120 changes: 91 additions & 29 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 32 additions & 29 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,38 @@
"@internationalized/date": "^3.7.0",
"@shopify/draggable": "^1.0.0-beta.12",
"@simplewebauthn/browser": "^13.2.2",
"@tiptap/core": "^3.0.0",
"@tiptap/extension-blockquote": "^3.0.0",
"@tiptap/extension-bold": "^3.0.0",
"@tiptap/extension-bullet-list": "^3.0.0",
"@tiptap/extension-character-count": "^3.0.0",
"@tiptap/extension-code": "^3.0.0",
"@tiptap/extension-code-block": "^3.0.0",
"@tiptap/extension-code-block-lowlight": "^3.0.0",
"@tiptap/extension-document": "^3.0.0",
"@tiptap/extension-hard-break": "^3.0.0",
"@tiptap/extension-heading": "^3.0.0",
"@tiptap/extension-history": "^3.0.0",
"@tiptap/extension-horizontal-rule": "^3.0.0",
"@tiptap/extension-italic": "^3.0.0",
"@tiptap/extension-list": "^3.0.0",
"@tiptap/extension-list-item": "^3.0.0",
"@tiptap/extension-ordered-list": "^3.0.0",
"@tiptap/extension-paragraph": "^3.0.0",
"@tiptap/extension-strike": "^3.0.0",
"@tiptap/extension-subscript": "^3.0.0",
"@tiptap/extension-superscript": "^3.0.0",
"@tiptap/extension-table": "^3.0.0",
"@tiptap/extension-text": "^3.0.0",
"@tiptap/extension-text-align": "^3.0.0",
"@tiptap/extension-typography": "^3.0.0",
"@tiptap/extension-underline": "^3.0.0",
"@tiptap/extensions": "^3.0.0",
"@tiptap/pm": "^3.0.0",
"@tiptap/vue-3": "^3.0.0",
"@tiptap/core": "^3.28.0",
"@tiptap/extension-blockquote": "^3.28.0",
"@tiptap/extension-bold": "^3.28.0",
"@tiptap/extension-bullet-list": "^3.28.0",
"@tiptap/extension-character-count": "^3.28.0",
"@tiptap/extension-code": "^3.28.0",
"@tiptap/extension-code-block": "^3.28.0",
"@tiptap/extension-code-block-lowlight": "^3.28.0",
"@tiptap/extension-document": "^3.28.0",
"@tiptap/extension-hard-break": "^3.28.0",
"@tiptap/extension-heading": "^3.28.0",
"@tiptap/extension-history": "^3.28.0",
"@tiptap/extension-horizontal-rule": "^3.28.0",
"@tiptap/extension-italic": "^3.28.0",
"@tiptap/extension-list": "^3.28.0",
"@tiptap/extension-list-item": "^3.28.0",
"@tiptap/extension-mention": "^3.28.0",
"@tiptap/extension-ordered-list": "^3.28.0",
"@tiptap/extension-paragraph": "^3.28.0",
"@tiptap/extension-strike": "^3.28.0",
"@tiptap/extension-subscript": "^3.28.0",
"@tiptap/extension-superscript": "^3.28.0",
"@tiptap/extension-table": "^3.28.0",
"@tiptap/extension-text": "^3.28.0",
"@tiptap/extension-text-align": "^3.28.0",
"@tiptap/extension-typography": "^3.28.0",
"@tiptap/extension-underline": "^3.28.0",
"@tiptap/extensions": "^3.28.0",
"@tiptap/markdown": "^3.29.2",
"@tiptap/pm": "^3.28.0",
"@tiptap/suggestion": "^3.28.0",
"@tiptap/vue-3": "^3.28.0",
"alpinejs": "^3.1.1",
"aria-hidden": "^1.2.6",
"autosize": "~6.0.1",
Expand Down
1 change: 1 addition & 0 deletions packages/cms/src/ui.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const {
Alert,
AuthCard,
AutocompleteEditor,
Avatar,
Badge,
Button,
Expand Down
1 change: 1 addition & 0 deletions resources/js/bootstrap/cms/ui.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export {
Alert,
AuthCard,
AutocompleteEditor,
Avatar,
Badge,
Button,
Expand Down
2 changes: 2 additions & 0 deletions resources/js/bootstrap/fieldtypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ArrayFieldtype from '../components/fieldtypes/ArrayFieldtype.vue';
import AssetsFieldtype from '../components/fieldtypes/assets/AssetsFieldtype.vue';
import AssetsIndexFieldtype from '../components/fieldtypes/assets/AssetsIndexFieldtype.vue';
import AssetFolderFieldtype from '../components/fieldtypes/AssetFolderFieldtype.vue';
import AutocompleteEditorFieldtype from '../components/fieldtypes/AutocompleteEditorFieldtype.vue';
import BardButtonsSettingFieldtype from '../components/fieldtypes/bard/BardButtonsSettingFieldtype.vue';
import BlueprintsFieldtype from '../components/fieldtypes/BlueprintsFieldtype.vue';
import ButtonGroupFieldtype from '../components/fieldtypes/ButtonGroupFieldtype.vue';
Expand Down Expand Up @@ -72,6 +73,7 @@ export default function registerFieldtypes(app) {
app.component('assets-fieldtype', AssetsFieldtype);
app.component('assets-fieldtype-index', AssetsIndexFieldtype);
app.component('asset_folder-fieldtype', AssetFolderFieldtype);
app.component('autocomplete_editor-fieldtype', AutocompleteEditorFieldtype);
app.component(
'bard-fieldtype',
defineAsyncComponent(() => import('../components/fieldtypes/bard/BardFieldtype.vue')),
Expand Down
55 changes: 55 additions & 0 deletions resources/js/components/fieldtypes/AutocompleteEditorFieldtype.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<AutocompleteEditor
:model-value="content"
:options="meta.options"
:inline="markdown ? config.inline : true"
:enable-line-breaks="config.enable_line_breaks"
:buttons="markdown ? config.buttons : []"
:trigger="config.trigger"
:placeholder="config.placeholder"
:read-only="isReadOnly"
@update:model-value="updateContent"
/>
</template>

<script setup>
import { computed } from 'vue';
import Fieldtype from '@/components/fieldtypes/fieldtype.js';
import { AutocompleteEditor } from '@/components/ui';
import { contentToMarkdown, markdownToContent } from '@/components/ui/AutocompleteEditor/markdown';
import { contentToText, textToContent } from '@/components/ui/AutocompleteEditor/text';

const emit = defineEmits(Fieldtype.emits);
const props = defineProps(Fieldtype.props);
const { isReadOnly, update } = Fieldtype.use(emit, props);

// Without markdown the editor is limited to a single paragraph of text and mentions,
// so there's no formatting for the schema to allow and nothing for a toolbar to do.
const markdown = props.config.markdown !== false;
const parse = markdown ? markdownToContent : textToContent;
const serialize = markdown ? contentToMarkdown : contentToText;

// Mention labels aren't stored, only the value, so they're
// resolved from the configured options every time content is loaded.
function hydrateMentionLabels(nodes) {
const labels = Object.fromEntries(props.meta.options.map((option) => [option.value, option.label]));

return nodes.map((node) => {
if (node.type === 'mention') {
const label = labels[node.attrs.value];

return label ? { ...node, attrs: { ...node.attrs, label } } : node;
}

if (node.content) {
return { ...node, content: hydrateMentionLabels(node.content) };
}

return node;
});
}

const content = computed(() => (props.value ? hydrateMentionLabels(parse(props.value)) : []));

const updateContent = (content) => update(serialize(content));
</script>
Loading
Loading