@blokkli/editor 2.0.0-alpha.46 → 2.0.0-alpha.47
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/dist/module.json +1 -1
- package/dist/module.mjs +2 -1
- package/dist/modules/agent/runtime/app/helpers/validation.d.ts +13 -0
- package/dist/modules/agent/runtime/app/helpers/validation.js +22 -0
- package/dist/modules/agent/runtime/app/tools/add_content_search_paragraph/index.js +12 -0
- package/dist/modules/agent/runtime/app/tools/add_fragment/index.js +12 -1
- package/dist/modules/agent/runtime/app/tools/add_media_paragraph/index.js +12 -0
- package/dist/modules/agent/runtime/app/tools/add_paragraphs/index.js +10 -0
- package/dist/modules/agent/runtime/app/tools/add_reusable_paragraph/index.js +12 -0
- package/dist/modules/agent/runtime/app/tools/add_template/index.js +5 -0
- package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/index.js +15 -0
- package/dist/modules/agent/runtime/app/tools/delete_paragraphs/index.js +12 -0
- package/dist/modules/agent/runtime/app/tools/detach_reusable_paragraph/index.js +12 -0
- package/dist/modules/agent/runtime/app/tools/duplicate_paragraphs/index.js +16 -1
- package/dist/modules/agent/runtime/app/tools/move_paragraphs/index.js +17 -0
- package/dist/modules/agent/runtime/app/tools/rearrange_paragraphs/index.js +11 -0
- package/dist/modules/agent/runtime/app/tools/replace_content_search_item/index.js +8 -0
- package/dist/modules/agent/runtime/app/tools/replace_media_field/index.js +10 -0
- package/dist/modules/agent/runtime/app/tools/set_paragraph_options/index.js +10 -0
- package/dist/modules/agent/runtime/app/tools/swap_paragraphs/index.js +15 -0
- package/dist/modules/agent/runtime/app/tools/update_text_fields/index.js +21 -1
- package/dist/modules/agent/runtime/app/types/index.d.ts +6 -6
- package/dist/modules/drupal/index.mjs +2 -1
- package/dist/modules/drupal/runtime/adapter/index.js +15 -3
- package/dist/runtime/editor/components/Actions/index.vue +47 -2
- package/dist/runtime/editor/components/AnimationCanvas/index.vue +6 -3
- package/dist/runtime/editor/components/BundleSelector/index.d.vue.ts +8 -4
- package/dist/runtime/editor/components/BundleSelector/index.vue +111 -13
- package/dist/runtime/editor/components/BundleSelector/index.vue.d.ts +8 -4
- package/dist/runtime/editor/components/EditProvider.vue +2 -2
- package/dist/runtime/editor/components/FlexTextarea/index.vue +8 -1
- package/dist/runtime/editor/css/output.css +1 -1
- package/dist/runtime/editor/features/add-list/Blocks/index.vue +6 -3
- package/dist/runtime/editor/features/analyze/Renderer/index.vue +1 -1
- package/dist/runtime/editor/features/block-scheduler/index.vue +7 -1
- package/dist/runtime/editor/features/changelog/Dialog/index.vue +1 -1
- package/dist/runtime/editor/features/changelog/changelog.json +18 -10
- package/dist/runtime/editor/features/clipboard/index.vue +6 -1
- package/dist/runtime/editor/features/comments/AddForm/index.d.vue.ts +2 -2
- package/dist/runtime/editor/features/comments/AddForm/index.vue.d.ts +2 -2
- package/dist/runtime/editor/features/delete/index.vue +17 -2
- package/dist/runtime/editor/features/dragging-overlay/Renderer/index.vue +12 -2
- package/dist/runtime/editor/features/dragging-overlay/index.vue +5 -2
- package/dist/runtime/editor/features/duplicate/index.vue +23 -7
- package/dist/runtime/editor/features/edit/index.vue +29 -8
- package/dist/runtime/editor/features/editable-field/index.vue +15 -1
- package/dist/runtime/editor/features/fragments/index.vue +5 -2
- package/dist/runtime/editor/features/hover/Renderer/index.vue +19 -6
- package/dist/runtime/editor/features/hover/Renderer/vertex.glsl +5 -2
- package/dist/runtime/editor/features/library/index.vue +52 -8
- package/dist/runtime/editor/features/media-library/index.vue +7 -2
- package/dist/runtime/editor/features/multi-select/Renderer/index.vue +4 -1
- package/dist/runtime/editor/features/search/index.vue +7 -2
- package/dist/runtime/editor/features/selection/AddButtons/Renderer/index.vue +1 -1
- package/dist/runtime/editor/features/selection/AddButtons/index.vue +26 -2
- package/dist/runtime/editor/features/selection/Renderer/index.vue +23 -5
- package/dist/runtime/editor/features/selection/Renderer/vertex.glsl +5 -2
- package/dist/runtime/editor/features/selection/index.vue +17 -5
- package/dist/runtime/editor/features/translations/index.vue +17 -11
- package/dist/runtime/editor/helpers/dropTargets/index.d.ts +1 -1
- package/dist/runtime/editor/helpers/dropTargets/index.js +2 -2
- package/dist/runtime/editor/plugins/ItemAction/index.d.vue.ts +4 -1
- package/dist/runtime/editor/plugins/ItemAction/index.vue +9 -3
- package/dist/runtime/editor/plugins/ItemAction/index.vue.d.ts +4 -1
- package/dist/runtime/editor/providers/permissions.d.ts +22 -1
- package/dist/runtime/editor/providers/permissions.js +99 -3
- package/dist/runtime/editor/providers/selection.d.ts +2 -1
- package/dist/runtime/editor/providers/selection.js +10 -5
- package/dist/runtime/editor/translations/de.json +89 -1
- package/dist/runtime/editor/translations/fr.json +89 -1
- package/dist/runtime/editor/translations/gsw_CH.json +89 -1
- package/dist/runtime/editor/translations/it.json +89 -1
- package/dist/runtime/editor/types/definitions.d.ts +2 -0
- package/package.json +1 -1
|
@@ -80,7 +80,8 @@ const {
|
|
|
80
80
|
$t,
|
|
81
81
|
state,
|
|
82
82
|
definitions,
|
|
83
|
-
blocks
|
|
83
|
+
blocks,
|
|
84
|
+
permissions
|
|
84
85
|
} = useBlokkli();
|
|
85
86
|
function buildItem(element) {
|
|
86
87
|
const itemBundle = element.dataset.sortliId;
|
|
@@ -112,7 +113,7 @@ function determineVisibility(bundle, label) {
|
|
|
112
113
|
}
|
|
113
114
|
const sortedList = computed(() => {
|
|
114
115
|
const autoAdd = definitions.bundlesWithAutoAdd.value;
|
|
115
|
-
return [...props.generallyAvailableBundles].filter((v) => !isInternalBundle(v.id)).map((v) => {
|
|
116
|
+
return [...props.generallyAvailableBundles].filter((v) => !isInternalBundle(v.id)).filter((v) => permissions.checkBlockBundlePermission(v.id, "add")).map((v) => {
|
|
116
117
|
const isVisible = determineVisibility(v.id, v.label);
|
|
117
118
|
const isDisabled = !v.id || !props.selectableBundles.includes(v.id);
|
|
118
119
|
const isAutoAdd = autoAdd.includes(v.id);
|
|
@@ -294,7 +295,9 @@ defineCommands(() => {
|
|
|
294
295
|
...getAppendCommands(),
|
|
295
296
|
...getInsertCommands(selection.items.value[0]),
|
|
296
297
|
...getAppendEndCommands()
|
|
297
|
-
]
|
|
298
|
+
].filter(
|
|
299
|
+
(v) => v.bundle && permissions.checkBlockBundlePermission(v.bundle, "add")
|
|
300
|
+
);
|
|
298
301
|
});
|
|
299
302
|
</script>
|
|
300
303
|
|
|
@@ -51,7 +51,7 @@ const {
|
|
|
51
51
|
readability
|
|
52
52
|
} = useBlokkli();
|
|
53
53
|
const showTooltip = computed(() => {
|
|
54
|
-
return !ui.isChangingOptions.value && !selection.isMultiSelecting.value && !selection.activeEditableLabel.value;
|
|
54
|
+
return !ui.isChangingOptions.value && !selection.isMultiSelecting.value && !selection.activeEditableLabel.value && !selection.isDragging.value;
|
|
55
55
|
});
|
|
56
56
|
const activeId = defineModel({ type: String, ...{
|
|
57
57
|
default: ""
|
|
@@ -79,7 +79,13 @@ const disabled = computed(() => {
|
|
|
79
79
|
const hasSupport = selection.bundles.value.some(
|
|
80
80
|
(bundle) => bundlesWithPublish.value.includes(bundle) || bundlesWithUnpublish.value.includes(bundle)
|
|
81
81
|
);
|
|
82
|
-
|
|
82
|
+
if (!hasSupport) {
|
|
83
|
+
return $t(
|
|
84
|
+
"schedulerNotSupported",
|
|
85
|
+
"Scheduling is not available for this block type."
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
83
89
|
});
|
|
84
90
|
function onClick() {
|
|
85
91
|
selectedUuids.value = [...selection.uuids.value];
|
|
@@ -1,42 +1,50 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "2.0.0-alpha.47",
|
|
4
|
+
"date": "2026-03-18",
|
|
5
|
+
"body": {
|
|
6
|
+
"en": "<h3>New Features</h3>\n<h4>Block permissions</h4>\n<p>Administrators can now restrict which block types individual users are allowed to\ncreate, edit, or delete. Restricted blocks are visually marked in the editor.</p>\n<h4>Fragments in block selector</h4>\n<p>When adding a block before or after a selected block, available fragments are now\nlisted directly in the selector by name. Clicking a fragment adds it immediately\nwithout opening a separate dialog.</p>\n<h3>Improvements</h3>\n<ul>\n<li>The block selector now groups items into separate sections for blocks,\nfragments, and actions, making it easier to find the right entry.</li>\n<li>The block selector adjusts its height to the available viewport space, reducing\nunnecessary scrolling when it opens near the top of the screen.</li>\n</ul>\n<h3>Fixes</h3>\n<ul>\n<li>Fixed the analyze tooltip not being positioned and styled correctly.</li>\n<li>Fixed the tooltip transition playing with the wrong origin when the tooltip\nopens above the anchor.</li>\n<li>Fixed the add button tooltip staying visible while the block selector is open.</li>\n</ul>\n",
|
|
7
|
+
"de": "<h3>Neue Funktionen</h3>\n<h4>Block-Berechtigungen</h4>\n<p>Administratoren können jetzt festlegen, welche Blocktypen einzelne Benutzer\nerstellen, bearbeiten oder löschen dürfen. Eingeschränkte Blöcke werden im\nEditor visuell markiert.</p>\n<h4>Fragmente in der Block-Auswahl</h4>\n<p>Beim Hinzufügen eines Blocks vor oder nach einem ausgewählten Block werden\nverfügbare Fragmente jetzt direkt namentlich in der Auswahl angezeigt. Ein Klick\nauf ein Fragment fügt es sofort hinzu, ohne einen separaten Dialog zu öffnen.</p>\n<h3>Verbesserungen</h3>\n<ul>\n<li>Die Block-Auswahl zeigt Einträge jetzt in getrennten Bereichen für Blöcke,\nFragmente und Aktionen an, was die Übersicht verbessert.</li>\n<li>Die Block-Auswahl passt ihre Höhe an den verfügbaren Platz im Fenster an, was\nunnötiges Scrollen beim Öffnen im oberen Bildschirmbereich reduziert.</li>\n</ul>\n<h3>Fehlerbehebungen</h3>\n<ul>\n<li>Der Analyse-Tooltip wurde nicht korrekt positioniert und dargestellt.</li>\n<li>Die Tooltip-Animation hatte den falschen Ursprung, wenn der Tooltip oberhalb\ndes Ankers geöffnet wurde.</li>\n<li>Der Tooltip der Hinzufügen-Schaltfläche blieb sichtbar, während die\nBlock-Auswahl geöffnet war.</li>\n</ul>\n"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
2
10
|
{
|
|
3
11
|
"version": "2.0.0-alpha.46",
|
|
4
12
|
"date": "2026-03-13",
|
|
5
13
|
"body": {
|
|
6
|
-
"en": "<h3>New Features</h3>\n<h4>Automatic text replacements</h4>\n<p>When editing text fields, common typographic patterns are now automatically
|
|
7
|
-
"de": "<h3>Neue Funktionen</h3>\n<h4>Automatische Textersetzungen</h4>\n<p>Beim Bearbeiten von Textfeldern werden häufige typografische Muster automatisch
|
|
14
|
+
"en": "<h3>New Features</h3>\n<h4>Automatic text replacements</h4>\n<p>When editing text fields, common typographic patterns are now automatically\nreplaced: straight double quotes ("...") become guillemets («...»), three dots\n(...) become an ellipsis (…), and double hyphens (--) become an en dash (–).\nGuillemet replacement is only active for German and French languages. All\nreplacements can be undone with Ctrl+Z.</p>\n<h4>Undo/redo in text fields</h4>\n<p>Text fields now have a proper undo/redo history. Use Ctrl+Z to undo and\nCtrl+Shift+Z or Ctrl+Y to redo.</p>\n",
|
|
15
|
+
"de": "<h3>Neue Funktionen</h3>\n<h4>Automatische Textersetzungen</h4>\n<p>Beim Bearbeiten von Textfeldern werden häufige typografische Muster automatisch\nersetzt: Gerade Anführungszeichen ("...") werden zu Guillemets («...»), drei\nPunkte (...) zu einem Auslassungszeichen (…) und doppelte Bindestriche (--) zu\neinem Gedankenstrich (–). Die Guillemet-Ersetzung ist nur bei deutscher und\nfranzösischer Sprache aktiv. Alle Ersetzungen können mit Ctrl+Z rückgängig\ngemacht werden.</p>\n<h4>Rückgängig/Wiederherstellen in Textfeldern</h4>\n<p>Textfelder haben jetzt eine vollständige Rückgängig-/Wiederherstellen-Historie.\nMit Ctrl+Z kann rückgängig gemacht und mit Ctrl+Shift+Z oder Ctrl+Y\nwiederhergestellt werden.</p>\n"
|
|
8
16
|
}
|
|
9
17
|
},
|
|
10
18
|
{
|
|
11
19
|
"version": "2.0.0-alpha.44",
|
|
12
20
|
"date": "2026-03-12",
|
|
13
21
|
"body": {
|
|
14
|
-
"en": "<h3>New Features</h3>\n<h4>Heading structure analyzer</h4>\n<p>A new built-in analyzer checks the heading hierarchy on your page (H1–H6) and
|
|
15
|
-
"de": "<h3>Neue Funktionen</h3>\n<h4>Überschriftenstruktur-Analyse</h4>\n<p>Eine neue Prüfung kontrolliert die Überschriftenstruktur auf der Seite (H1–H6)
|
|
22
|
+
"en": "<h3>New Features</h3>\n<h4>Heading structure analyzer</h4>\n<p>A new built-in analyzer checks the heading hierarchy on your page (H1–H6) and\nflags issues such as multiple H1 headings or skipped heading levels.</p>\n<h4>Image alt text analyzer</h4>\n<p>A new accessibility analyzer highlights images that are missing alt text\ndirectly on the page.</p>\n<h4>Analyze tooltips on the artboard</h4>\n<p>When the analyze panel is active, hovering over a highlighted element now shows\na tooltip with the issue title and readability score.</p>\n<h4>Readability score in editable overlay</h4>\n<p>While editing a rich text field, the readability score for the full text is now\nshown in the editing toolbar above the field.</p>\n<h4>Select parent block button</h4>\n<p>The block actions toolbar now has a button that lets you quickly select the\nenclosing parent block.</p>\n<h4>Move block button</h4>\n<p>A drag handle button in the actions toolbar lets you start moving blocks\ndirectly from the toolbar.</p>\n<h4>Improved search when adding blocks</h4>\n<p>The block selector now finds the right block type even with typos or partial\nterms. The search field is also focused automatically when the selector opens.</p>\n<h3>Improvements</h3>\n<ul>\n<li>The "Keep results visible" toggle in the analyze panel now shows a description\nexplaining that results stay highlighted even after the panel is closed.</li>\n</ul>\n<h3>Fixes</h3>\n<ul>\n<li>Fixed the edit indicator not rendering after entering edit mode.</li>\n<li>Fixed the sticky block actions toolbar sometimes jumping to the wrong\nposition.</li>\n<li>Fixed a visual glitch in the selection when the page was selected and the\ncontext menu was opened.</li>\n<li>Fixed block options changes not being submitted when a dialog was opened.</li>\n<li>Fixed the language switcher being shown even when only one language is\navailable.</li>\n<li>Fixed readability scores being calculated incorrectly for certain text fields.</li>\n<li>Improved the readability analysis for German texts.</li>\n</ul>\n",
|
|
23
|
+
"de": "<h3>Neue Funktionen</h3>\n<h4>Überschriftenstruktur-Analyse</h4>\n<p>Eine neue Prüfung kontrolliert die Überschriftenstruktur auf der Seite (H1–H6)\nund markiert Probleme wie mehrfache H1-Überschriften oder übersprungene Ebenen.</p>\n<h4>Alt-Text-Analyse für Bilder</h4>\n<p>Eine neue Barrierefreiheits-Prüfung hebt Bilder ohne Alt-Text direkt auf der\nSeite hervor.</p>\n<h4>Analyse-Tooltips auf der Seite</h4>\n<p>Wenn das Analyse-Panel aktiv ist, erscheint beim Hovern über ein markiertes\nElement ein Tooltip mit dem Titel des Problems und dem Lesbarkeitswert.</p>\n<h4>Lesbarkeitswert in der Bearbeitungsansicht</h4>\n<p>Beim Bearbeiten eines Rich-Text-Feldes wird der Lesbarkeitswert in der\nWerkzeugleiste oberhalb des Feldes angezeigt.</p>\n<h4>Übergeordneten Block auswählen</h4>\n<p>Die Block-Aktionsleiste hat jetzt eine Schaltfläche, um schnell den\nübergeordneten Block auszuwählen.</p>\n<h4>Block-Verschieben-Schaltfläche</h4>\n<p>Ein Ziehpunkt in der Aktionsleiste ermöglicht das direkte Verschieben von\nBlöcken.</p>\n<h4>Verbesserte Suche beim Hinzufügen von Blöcken</h4>\n<p>Die Block-Auswahl findet jetzt auch bei Tippfehlern den richtigen Blocktyp. Das\nSuchfeld wird beim Öffnen automatisch fokussiert.</p>\n<h3>Verbesserungen</h3>\n<ul>\n<li>Der Schalter «Ergebnisse sichtbar lassen» im Analyse-Panel zeigt jetzt eine\nBeschreibung, die erklärt, dass Ergebnisse auch nach dem Schliessen des Panels\nauf der Seite hervorgehoben bleiben.</li>\n</ul>\n<h3>Fehlerbehebungen</h3>\n<ul>\n<li>Der Bearbeitungsindikator wurde nach dem Wechsel in den Bearbeitungsmodus\nnicht angezeigt.</li>\n<li>Die fixierte Block-Aktionsleiste sprang manchmal an die falsche Position.</li>\n<li>Ein visueller Fehler bei der Auswahl wurde behoben, der beim gleichzeitigen\nÖffnen des Kontextmenüs auftrat.</li>\n<li>Block-Optionen wurden beim Öffnen eines Dialogs nicht übermittelt.</li>\n<li>Der Sprachumschalter wurde auch dann angezeigt, wenn nur eine Sprache\nverfügbar war.</li>\n<li>Lesbarkeitswerte wurden in gewissen Fällen falsch berechnet.</li>\n<li>Die Lesbarkeitsanalyse für deutsche Texte wurde verbessert.</li>\n</ul>\n"
|
|
16
24
|
}
|
|
17
25
|
},
|
|
18
26
|
{
|
|
19
27
|
"version": "2.0.0-alpha.39",
|
|
20
28
|
"date": "2026-02-22",
|
|
21
29
|
"body": {
|
|
22
|
-
"en": "<h3>New Features</h3>\n<h4>AI Assistant file attachments</h4>\n<p>Drag and drop files (including Word documents) directly onto the AI chat panel
|
|
23
|
-
"de": "<h3>Neue Funktionen</h3>\n<h4>KI-Assistent: Dateianhänge</h4>\n<p>Dateien (inkl. Word-Dokumente) können direkt per Drag & Drop auf den KI-Chat
|
|
30
|
+
"en": "<h3>New Features</h3>\n<h4>AI Assistant file attachments</h4>\n<p>Drag and drop files (including Word documents) directly onto the AI chat panel\nto attach them as context — the content is automatically extracted and\nformatted.</p>\n<h4>AI readability analysis</h4>\n<p>The AI assistant can now analyze the page for readability issues and iteratively\nimprove texts based on readability scores.</p>\n<h4>Faster AI responses</h4>\n<p>The assistant now responds faster to the first message of a conversation.</p>\n<h4>AI action details</h4>\n<p>Completed AI actions in the conversation can now show an expandable details\npanel with additional context.</p>\n<h4>AI welcome popup</h4>\n<p>A welcome popup now introduces new users to the AI assistant when the editor\nopens.</p>\n<h3>Improvements</h3>\n<ul>\n<li>The AI assistant sidebar button has been moved to the bottom-right of the\ntoolbar and features an animated star icon.</li>\n<li>The "Start new conversation" and "Past conversations" buttons are now shown\ndirectly in the input area instead of in a dropdown.</li>\n<li>Double-clicking a complex data option (e.g. chart data) now immediately opens\nthe editor for that option.</li>\n<li>The AI assistant shows a reconnection indicator in the chat when the\nconnection drops temporarily.</li>\n</ul>\n<h3>Fixes</h3>\n<ul>\n<li>Selecting text inside an inline-editable field no longer accidentally closes\nthe editor when releasing the mouse outside the field.</li>\n<li>The middle mouse button no longer interrupts drag-and-drop interactions.</li>\n<li>Incomplete or truncated AI responses are now handled gracefully instead of\nsilently failing.</li>\n</ul>\n",
|
|
31
|
+
"de": "<h3>Neue Funktionen</h3>\n<h4>KI-Assistent: Dateianhänge</h4>\n<p>Dateien (inkl. Word-Dokumente) können direkt per Drag & Drop auf den KI-Chat\ngezogen werden — der Inhalt wird automatisch extrahiert und formatiert.</p>\n<h4>KI-Lesbarkeitsanalyse</h4>\n<p>Der KI-Assistent kann jetzt die Seite auf Lesbarkeitsprobleme prüfen und Texte\nanhand der Lesbarkeitswerte schrittweise verbessern.</p>\n<h4>Schnellere KI-Antworten</h4>\n<p>Der Assistent antwortet jetzt schneller auf die erste Nachricht eines Gesprächs.</p>\n<h4>KI-Aktionsdetails</h4>\n<p>Abgeschlossene KI-Aktionen im Gesprächsverlauf können jetzt aufgeklappt werden,\num mehr Details anzuzeigen.</p>\n<h4>KI-Willkommens-Popup</h4>\n<p>Beim Öffnen des Editors erscheint ein Willkommens-Popup, das neue Nutzer in den\nKI-Assistenten einführt.</p>\n<h3>Verbesserungen</h3>\n<ul>\n<li>Die Schaltfläche des KI-Assistenten wurde in die rechte untere Ecke der\nWerkzeugleiste verschoben und zeigt jetzt ein animiertes Stern-Symbol.</li>\n<li>Die Schaltflächen «Neues Gespräch» und «Vergangene Gespräche» werden jetzt\ndirekt im Eingabebereich angezeigt statt in einem Dropdown.</li>\n<li>Ein Doppelklick auf eine komplexe Datenoption (z. B. Diagrammdaten) öffnet\njetzt direkt den zugehörigen Editor.</li>\n<li>Der KI-Assistent zeigt im Chat einen Hinweis an, wenn die Verbindung\nvorübergehend unterbrochen wird.</li>\n</ul>\n<h3>Fehlerbehebungen</h3>\n<ul>\n<li>Textauswahl in einem Inline-Bearbeitungsfeld schliesst den Editor nicht mehr\nversehentlich, wenn die Maus ausserhalb des Felds losgelassen wird.</li>\n<li>Die mittlere Maustaste unterbricht Drag-&-Drop-Aktionen nicht mehr.</li>\n<li>Unvollständige oder abgebrochene KI-Antworten werden jetzt korrekt behandelt,\nanstatt ohne Fehlermeldung abzubrechen.</li>\n</ul>\n"
|
|
24
32
|
}
|
|
25
33
|
},
|
|
26
34
|
{
|
|
27
35
|
"version": "2.0.0-alpha.35",
|
|
28
36
|
"date": "2026-02-11",
|
|
29
37
|
"body": {
|
|
30
|
-
"en": "<h3>New Features</h3>\n<h4>AI Assistant</h4>\n<p>A new AI-powered assistant is available in the editor sidebar, allowing you to
|
|
31
|
-
"de": "<h3>Neue Funktionen</h3>\n<h4>KI-Assistent</h4>\n<p>Im Editor steht ein neuer KI-Assistent zur Verfügung. Damit können Blöcke per
|
|
38
|
+
"en": "<h3>New Features</h3>\n<h4>AI Assistant</h4>\n<p>A new AI-powered assistant is available in the editor sidebar, allowing you to\nadd, move, delete, and rewrite content blocks through a chat interface with\nsupport for plans, conversation history, and media selection.</p>\n<h4>Block preview in "Add" panel</h4>\n<p>Hovering a block type in the add panel now shows a tooltip with a preview image\nand description.</p>\n<h4>Template management</h4>\n<p>A new management view lets you rename and delete saved templates directly from\nthe editor.</p>\n<h4>Charts</h4>\n<p>A new chart block type is available for embedding interactive charts on pages.</p>\n<h4>Table of contents</h4>\n<p>A new table of contents module is available.</p>\n<h3>Improvements</h3>\n<ul>\n<li>You can now hold Ctrl and click to select multiple items in the media library\nat once.</li>\n<li>Dragged items are now sized more accurately to match the block being moved.</li>\n<li>Drop target areas shrink visually when not hovered, giving a cleaner layout\nwhile dragging.</li>\n</ul>\n<h3>Fixes</h3>\n<ul>\n<li>Long option descriptions no longer overflow in the options tooltip.</li>\n<li>Hover detection for overlapping blocks now correctly respects z-index order.</li>\n<li>The default icon is now correctly displayed for fragment blocks.</li>\n<li>Opening the AI assistant from a block action no longer restores a previous\nconversation; it starts fresh.</li>\n</ul>\n",
|
|
39
|
+
"de": "<h3>Neue Funktionen</h3>\n<h4>KI-Assistent</h4>\n<p>Im Editor steht ein neuer KI-Assistent zur Verfügung. Damit können Blöcke per\nChat hinzugefügt, verschoben, gelöscht und umgeschrieben werden – inklusive\nPlanung, Gesprächsverlauf und Medienauswahl.</p>\n<h4>Block-Vorschau im «Hinzufügen»-Panel</h4>\n<p>Beim Überfahren eines Blocktyps im Hinzufügen-Panel erscheint ein Tooltip mit\nVorschau und Beschreibung.</p>\n<h4>Vorlagenverwaltung</h4>\n<p>In einer neuen Verwaltungsansicht können gespeicherte Vorlagen direkt im Editor\numbenannt und gelöscht werden.</p>\n<h4>Diagramme</h4>\n<p>Ein neuer Diagramm-Blocktyp ist verfügbar, um interaktive Diagramme auf Seiten\neinzubetten.</p>\n<h4>Inhaltsverzeichnis</h4>\n<p>Ein neues Inhaltsverzeichnis-Modul ist verfügbar.</p>\n<h3>Verbesserungen</h3>\n<ul>\n<li>Mit Strg+Klick können jetzt mehrere Elemente in der Medienbibliothek\ngleichzeitig ausgewählt werden.</li>\n<li>Beim Ziehen werden Elemente jetzt passender in der Grösse des verschobenen\nBlocks dargestellt.</li>\n<li>Ablagezonen werden kleiner dargestellt, wenn sie nicht aktiv überfahren\nwerden, was beim Ziehen für ein aufgeräumteres Layout sorgt.</li>\n</ul>\n<h3>Fehlerbehebungen</h3>\n<ul>\n<li>Lange Optionsbeschreibungen im Optionen-Tooltip werden jetzt korrekt\ndargestellt und laufen nicht mehr über.</li>\n<li>Überlappende Blöcke reagieren jetzt korrekt auf Hover-Aktionen.</li>\n<li>Das Standardsymbol für Fragment-Blöcke wird jetzt korrekt angezeigt.</li>\n<li>Der KI-Assistent startet jetzt immer ein neues Gespräch, wenn er über das\nDropdown-Menü eines Blocks geöffnet wird.</li>\n</ul>\n"
|
|
32
40
|
}
|
|
33
41
|
},
|
|
34
42
|
{
|
|
35
43
|
"version": "2.0.0-alpha.28",
|
|
36
44
|
"date": "2026-01-01",
|
|
37
45
|
"body": {
|
|
38
|
-
"en": "<h3>New Features</h3>\n<h4>Templates</h4>\n<p>Blocks can now be saved as reusable templates and inserted into any field from
|
|
39
|
-
"de": "<h3>Neue Funktionen</h3>\n<h4>Vorlagen</h4>\n<p>Blöcke können jetzt als Vorlagen gespeichert und aus der Hinzufügen-Liste in
|
|
46
|
+
"en": "<h3>New Features</h3>\n<h4>Templates</h4>\n<p>Blocks can now be saved as reusable templates and inserted into any field from\nthe add list, with a manage dialog for browsing, creating, and organizing\ntemplates.</p>\n<h4>Selection breadcrumb</h4>\n<p>A breadcrumb bar appears when a block is selected, showing the nesting path and\nletting you navigate to parent fields or the page with a single click.</p>\n<h4>Empty field buttons when host is selected</h4>\n<p>Quick-add buttons for empty fields now also appear when the page itself is\nselected, so you can add blocks without first selecting a block.</p>\n<h4>Search in "Add" overlay</h4>\n<p>The overlay that appears when clicking an add button now includes a search field\nto quickly filter available blocks.</p>\n<h3>Improvements</h3>\n<ul>\n<li>Pressing the space bar while hovering the canvas now activates the move/pan\ncursor, matching common design tool conventions.</li>\n<li>After duplicating blocks, the duplicated blocks are automatically selected and\nscrolled into view.</li>\n<li>After deleting a block, the page scrolls to bring the nearest remaining block\ninto view.</li>\n<li>The dragging overlay label now shows the nesting trail (e.g. parent block\nname) and stays visible at viewport edges.</li>\n<li>The analyze category filter is hidden when there is only one category.</li>\n<li>The app menu now scrolls correctly when the list of items is longer than the\nvisible area.</li>\n</ul>\n<h3>Fixes</h3>\n<ul>\n<li>Fixed the analyze button being shown when no analyzers were available.</li>\n<li>Fixed the transform preview not displaying the result correctly before\napplying.</li>\n<li>Fixed the add list panel being too tall on small-screen viewports.</li>\n<li>Fixed the "New" label not appearing in the action bar when multiple blocks are\nselected.</li>\n</ul>\n",
|
|
47
|
+
"de": "<h3>Neue Funktionen</h3>\n<h4>Vorlagen</h4>\n<p>Blöcke können jetzt als Vorlagen gespeichert und aus der Hinzufügen-Liste in\nbeliebige Felder eingefügt werden. Ein Dialog zum Verwalten, Durchsuchen und\nErstellen von Vorlagen ist ebenfalls verfügbar.</p>\n<h4>Auswahl-Breadcrumb</h4>\n<p>Beim Auswählen eines Blocks erscheint eine Breadcrumb-Leiste, die den\nVerschachtelungspfad anzeigt. So kann man schnell zu übergeordneten Feldern oder\nzur Seite navigieren.</p>\n<h4>Leere-Feld-Schaltflächen bei ausgewählter Seite</h4>\n<p>Schnell-Hinzufügen-Schaltflächen für leere Felder erscheinen jetzt auch, wenn\ndie Seite selbst ausgewählt ist.</p>\n<h4>Suche im «Hinzufügen»-Overlay</h4>\n<p>Das Overlay beim Klicken auf eine Hinzufügen-Schaltfläche enthält jetzt ein\nSuchfeld, um die verfügbaren Blöcke schnell zu filtern.</p>\n<h3>Verbesserungen</h3>\n<ul>\n<li>Die Leertaste aktiviert über der Arbeitsfläche jetzt den\nVerschiebe-/Pan-Cursor, wie in gängigen Design-Tools üblich.</li>\n<li>Nach dem Duplizieren werden die neuen Blöcke automatisch ausgewählt und in den\nSichtbereich gescrollt.</li>\n<li>Nach dem Löschen eines Blocks scrollt die Seite automatisch zum nächsten\nverbleibenden Block.</li>\n<li>Das Zieh-Overlay zeigt jetzt den Verschachtelungspfad und bleibt am\nViewport-Rand sichtbar.</li>\n<li>Das Dropdown zur Analyse-Kategorieauswahl wird ausgeblendet, wenn nur eine\nKategorie vorhanden ist.</li>\n<li>Das App-Menü scrollt jetzt korrekt, wenn die Liste länger als der sichtbare\nBereich ist.</li>\n</ul>\n<h3>Fehlerbehebungen</h3>\n<ul>\n<li>Die Analyse-Schaltfläche wurde angezeigt, obwohl keine Analysen verfügbar\nwaren.</li>\n<li>Die Vorschau von Transformationen wurde korrigiert.</li>\n<li>Die Hinzufügen-Liste war auf kleinen Bildschirmen zu gross.</li>\n<li>Die Bezeichnung «Neu» erschien nicht in der Aktionsleiste, wenn mehrere Blöcke\nausgewählt waren.</li>\n</ul>\n"
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
50
|
]
|
|
@@ -51,7 +51,8 @@ const {
|
|
|
51
51
|
blocks,
|
|
52
52
|
fields,
|
|
53
53
|
eventBus,
|
|
54
|
-
animation
|
|
54
|
+
animation,
|
|
55
|
+
permissions
|
|
55
56
|
} = useBlokkli();
|
|
56
57
|
const selectionClipboard = ref([]);
|
|
57
58
|
const isDirectDrop = ref(false);
|
|
@@ -353,6 +354,10 @@ const handleSelectionPaste = (pastedUuids) => {
|
|
|
353
354
|
if (!existingBlocks.length) {
|
|
354
355
|
return;
|
|
355
356
|
}
|
|
357
|
+
const deniedBundles = existingBlocks.map((b) => b.bundle).filter((bundle) => !permissions.checkBlockBundlePermission(bundle, "add"));
|
|
358
|
+
if (deniedBundles.length) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
356
361
|
if (selection.uuids.value.length !== 1) {
|
|
357
362
|
startCopyDrag(existingBlocks);
|
|
358
363
|
return;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
declare const __VLS_export: import("vue").DefineComponent<{}, {
|
|
2
2
|
getComment: () => string;
|
|
3
3
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
4
|
-
close: () => any;
|
|
5
4
|
add: (comment: string) => any;
|
|
5
|
+
close: () => any;
|
|
6
6
|
}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
|
|
7
|
-
onClose?: (() => any) | undefined;
|
|
8
7
|
onAdd?: ((comment: string) => any) | undefined;
|
|
8
|
+
onClose?: (() => any) | undefined;
|
|
9
9
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
10
10
|
declare const _default: typeof __VLS_export;
|
|
11
11
|
export default _default;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
declare const __VLS_export: import("vue").DefineComponent<{}, {
|
|
2
2
|
getComment: () => string;
|
|
3
3
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
4
|
-
close: () => any;
|
|
5
4
|
add: (comment: string) => any;
|
|
5
|
+
close: () => any;
|
|
6
6
|
}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
|
|
7
|
-
onClose?: (() => any) | undefined;
|
|
8
7
|
onAdd?: ((comment: string) => any) | undefined;
|
|
8
|
+
onClose?: (() => any) | undefined;
|
|
9
9
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
10
10
|
declare const _default: typeof __VLS_export;
|
|
11
11
|
export default _default;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
id="delete"
|
|
4
4
|
edit-only
|
|
5
5
|
:title="$t('deleteButton', 'Delete')"
|
|
6
|
+
:disabled="deleteDisabledReason"
|
|
6
7
|
multiple
|
|
7
8
|
key-code="Delete"
|
|
8
9
|
icon="bk_mdi_delete"
|
|
@@ -12,10 +13,10 @@
|
|
|
12
13
|
</template>
|
|
13
14
|
|
|
14
15
|
<script setup>
|
|
15
|
-
import { useBlokkli, defineBlokkliFeature } from "#imports";
|
|
16
|
+
import { useBlokkli, defineBlokkliFeature, computed } from "#imports";
|
|
16
17
|
import { PluginItemAction } from "#blokkli/editor/plugins";
|
|
17
18
|
import { itemEntityType } from "#blokkli-build/config";
|
|
18
|
-
const { state, $t, eventBus, dom } = useBlokkli();
|
|
19
|
+
const { state, $t, eventBus, dom, selection, permissions } = useBlokkli();
|
|
19
20
|
const { adapter } = defineBlokkliFeature({
|
|
20
21
|
id: "delete",
|
|
21
22
|
icon: "bk_mdi_delete",
|
|
@@ -23,6 +24,20 @@ const { adapter } = defineBlokkliFeature({
|
|
|
23
24
|
requiredAdapterMethods: ["deleteBlocks"],
|
|
24
25
|
description: "Provides an action to delete one or more blocks."
|
|
25
26
|
});
|
|
27
|
+
const deleteDisabledReason = computed(() => {
|
|
28
|
+
const bundles = selection.bundles.value;
|
|
29
|
+
if (!bundles.length) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const denied = permissions.filterDeniedBundles(bundles, "delete");
|
|
33
|
+
if (denied.length) {
|
|
34
|
+
return $t(
|
|
35
|
+
"deleteNoPermission",
|
|
36
|
+
"You do not have permission to delete this block."
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
});
|
|
26
41
|
function getSelectionAfterDelete(items) {
|
|
27
42
|
if (items.length !== 1) {
|
|
28
43
|
return;
|
|
@@ -56,7 +56,8 @@ const {
|
|
|
56
56
|
types,
|
|
57
57
|
fields,
|
|
58
58
|
definitions,
|
|
59
|
-
context
|
|
59
|
+
context,
|
|
60
|
+
permissions
|
|
60
61
|
} = useBlokkli();
|
|
61
62
|
const FIELD_MIN_DRAW_SIZE = 6;
|
|
62
63
|
const alphaBase = 0.7;
|
|
@@ -158,6 +159,11 @@ const emitDrop = async () => {
|
|
|
158
159
|
}
|
|
159
160
|
eventBus.emit("dragging:end");
|
|
160
161
|
};
|
|
162
|
+
const isDraggingExisting = computed(
|
|
163
|
+
() => props.items.some(
|
|
164
|
+
(item) => item.itemType === "existing" || item.itemType === "existing_structure"
|
|
165
|
+
)
|
|
166
|
+
);
|
|
161
167
|
const draggingBundles = computed(
|
|
162
168
|
() => props.items.flatMap((item) => {
|
|
163
169
|
const bundles = [];
|
|
@@ -421,6 +427,9 @@ const buildFieldRect = (key) => {
|
|
|
421
427
|
if (!field) {
|
|
422
428
|
return;
|
|
423
429
|
}
|
|
430
|
+
if (field.hostEntityType === itemEntityType && (!permissions.checkBlockBundlePermission(field.hostEntityBundle, "edit") || permissions.blockHasRestrictedAncestor(field.hostEntityUuid))) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
424
433
|
const childElements = [...field.element.children];
|
|
425
434
|
const currentCount = state.getFieldBlockCount(field.key);
|
|
426
435
|
const canAddChildren = determineCanAddChildren(
|
|
@@ -430,7 +439,8 @@ const buildFieldRect = (key) => {
|
|
|
430
439
|
currentCount,
|
|
431
440
|
props.items.length,
|
|
432
441
|
draggingBundles.value,
|
|
433
|
-
draggingFragments.value
|
|
442
|
+
draggingFragments.value,
|
|
443
|
+
isDraggingExisting.value ? void 0 : (bundle) => permissions.checkBlockBundlePermission(bundle, "add")
|
|
434
444
|
);
|
|
435
445
|
const orientation = field.dropAlignment || getChildrenOrientation(field.element);
|
|
436
446
|
const rect = dom.getFieldRect(field.key);
|
|
@@ -72,7 +72,8 @@ const {
|
|
|
72
72
|
directive,
|
|
73
73
|
fields,
|
|
74
74
|
$t,
|
|
75
|
-
dragdrop
|
|
75
|
+
dragdrop,
|
|
76
|
+
permissions
|
|
76
77
|
} = useBlokkli();
|
|
77
78
|
const bundleSelectorData = ref(null);
|
|
78
79
|
let bundleSelectorResolve = null;
|
|
@@ -180,7 +181,9 @@ const onDrop = async (e) => {
|
|
|
180
181
|
};
|
|
181
182
|
let result;
|
|
182
183
|
if (handler.resolveBundles) {
|
|
183
|
-
const bundles = await handler.resolveBundles(baseCtx)
|
|
184
|
+
const bundles = (await handler.resolveBundles(baseCtx)).filter(
|
|
185
|
+
(b) => permissions.checkBlockBundlePermission(b, "add")
|
|
186
|
+
);
|
|
184
187
|
if (bundles.length === 0) {
|
|
185
188
|
return;
|
|
186
189
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<PluginItemAction
|
|
3
3
|
id="duplicate"
|
|
4
4
|
:title="$t('duplicate', 'Duplicate')"
|
|
5
|
-
:disabled="
|
|
5
|
+
:disabled="duplicateDisabledReason"
|
|
6
6
|
edit-only
|
|
7
7
|
meta
|
|
8
8
|
key-code="D"
|
|
@@ -18,7 +18,7 @@ import { computed, useBlokkli, defineBlokkliFeature } from "#imports";
|
|
|
18
18
|
import { PluginItemAction } from "#blokkli/editor/plugins";
|
|
19
19
|
import { getFieldKey } from "#blokkli/helpers";
|
|
20
20
|
import { getArrayDiff } from "#blokkli/editor/helpers/array";
|
|
21
|
-
const { state, $t, selection, types, eventBus } = useBlokkli();
|
|
21
|
+
const { state, $t, selection, types, eventBus, permissions } = useBlokkli();
|
|
22
22
|
const { adapter } = defineBlokkliFeature({
|
|
23
23
|
id: "duplicate",
|
|
24
24
|
icon: "duplicate",
|
|
@@ -45,7 +45,7 @@ async function onClick(items) {
|
|
|
45
45
|
const firstUuid = diff[0];
|
|
46
46
|
eventBus.emit("scrollIntoView", { uuid: firstUuid });
|
|
47
47
|
}
|
|
48
|
-
const
|
|
48
|
+
const duplicateDisabledReason = computed(() => {
|
|
49
49
|
const blocksByField = {};
|
|
50
50
|
const fieldsByKey = {};
|
|
51
51
|
const selectedCount = selection.items.value.length;
|
|
@@ -66,7 +66,10 @@ const canDuplicate = computed(() => {
|
|
|
66
66
|
}
|
|
67
67
|
const count = field.list.length;
|
|
68
68
|
if (fieldConfig.cardinality !== -1 && count >= fieldConfig.cardinality) {
|
|
69
|
-
return
|
|
69
|
+
return $t(
|
|
70
|
+
"duplicateFieldFull",
|
|
71
|
+
"The field has reached its maximum number of blocks."
|
|
72
|
+
);
|
|
70
73
|
}
|
|
71
74
|
if (!blocksByField[fieldKey]) {
|
|
72
75
|
blocksByField[fieldKey] = [];
|
|
@@ -87,14 +90,27 @@ const canDuplicate = computed(() => {
|
|
|
87
90
|
}
|
|
88
91
|
const count = state.getFieldBlockCount(fieldKey);
|
|
89
92
|
if (field.cardinality !== -1 && count + blocks.length > field.cardinality) {
|
|
90
|
-
return
|
|
93
|
+
return $t(
|
|
94
|
+
"duplicateFieldFull",
|
|
95
|
+
"The field has reached its maximum number of blocks."
|
|
96
|
+
);
|
|
91
97
|
}
|
|
92
98
|
const bundles = blocks.map((v) => v.bundle);
|
|
99
|
+
const denied = permissions.filterDeniedBundles(bundles, "add");
|
|
100
|
+
if (denied.length) {
|
|
101
|
+
return $t(
|
|
102
|
+
"duplicateNoPermission",
|
|
103
|
+
"You do not have permission to duplicate this block."
|
|
104
|
+
);
|
|
105
|
+
}
|
|
93
106
|
if (!field.allowedBundles.length || bundles.some((bundle) => !field.allowedBundles.includes(bundle))) {
|
|
94
|
-
return
|
|
107
|
+
return $t(
|
|
108
|
+
"duplicateNotAllowed",
|
|
109
|
+
"This block type is not allowed in this field."
|
|
110
|
+
);
|
|
95
111
|
}
|
|
96
112
|
}
|
|
97
|
-
return
|
|
113
|
+
return false;
|
|
98
114
|
});
|
|
99
115
|
</script>
|
|
100
116
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
id="edit"
|
|
4
4
|
edit-only
|
|
5
5
|
:title="$t('edit', 'Edit...')"
|
|
6
|
-
:disabled="
|
|
6
|
+
:disabled="editDisabledReason"
|
|
7
7
|
meta
|
|
8
8
|
key-code="E"
|
|
9
9
|
icon="bk_mdi_edit"
|
|
@@ -35,13 +35,13 @@ function isFragment(item) {
|
|
|
35
35
|
function isFeatureFragment(item) {
|
|
36
36
|
return !!item.fragment?.name && featureFragmentNames.includes(item.fragment.name);
|
|
37
37
|
}
|
|
38
|
-
const
|
|
38
|
+
const editDisabledReason = computed(() => {
|
|
39
39
|
const item = selection.item.value;
|
|
40
40
|
if (!item) {
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
43
|
if (isFragment(item)) {
|
|
44
|
-
return isFeatureFragment(item);
|
|
44
|
+
return isFeatureFragment(item) ? false : $t("editFragmentNotEditable", "This fragment cannot be edited.");
|
|
45
45
|
}
|
|
46
46
|
const definition = definitions.getBlockDefinition(
|
|
47
47
|
item.bundle,
|
|
@@ -49,21 +49,42 @@ const canEdit = computed(() => {
|
|
|
49
49
|
item.parentBlockBundle
|
|
50
50
|
);
|
|
51
51
|
if (definition?.editor?.disableEdit) {
|
|
52
|
-
return
|
|
52
|
+
return $t(
|
|
53
|
+
"editDisabledByDefinition",
|
|
54
|
+
"Editing is disabled for this block type."
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (!permissions.checkBlockBundlePermission(item.bundle, "edit")) {
|
|
58
|
+
return $t(
|
|
59
|
+
"editNoPermission",
|
|
60
|
+
"You do not have permission to edit this block."
|
|
61
|
+
);
|
|
53
62
|
}
|
|
54
63
|
if (item.library?.libraryItemUuid) {
|
|
55
64
|
if (!userCanEditLibraryItems.value) {
|
|
56
|
-
return
|
|
65
|
+
return $t(
|
|
66
|
+
"editNoLibraryPermission",
|
|
67
|
+
"You do not have permission to edit library items."
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (!adapter.getLibraryItemEditUrl || state.editMode.value !== "editing" && state.editMode.value !== "translating" || item.isNew) {
|
|
71
|
+
return $t(
|
|
72
|
+
"editLibraryNotAvailable",
|
|
73
|
+
"This reusable block cannot be edited right now."
|
|
74
|
+
);
|
|
57
75
|
}
|
|
58
|
-
return
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (state.editMode.value !== "editing") {
|
|
79
|
+
return false;
|
|
59
80
|
}
|
|
60
|
-
return
|
|
81
|
+
return false;
|
|
61
82
|
});
|
|
62
83
|
function onClick(items) {
|
|
63
84
|
if (items.length !== 1) {
|
|
64
85
|
return;
|
|
65
86
|
}
|
|
66
|
-
if (
|
|
87
|
+
if (editDisabledReason.value) {
|
|
67
88
|
return;
|
|
68
89
|
}
|
|
69
90
|
const item = items[0];
|
|
@@ -31,7 +31,18 @@ defineBlokkliFeature({
|
|
|
31
31
|
requiredAdapterMethods: ["updateFieldValue", "getEditableFieldConfig"],
|
|
32
32
|
description: "Implements a form overlay to edit a single field of a block."
|
|
33
33
|
});
|
|
34
|
-
const {
|
|
34
|
+
const {
|
|
35
|
+
selection,
|
|
36
|
+
adapter,
|
|
37
|
+
types,
|
|
38
|
+
$t,
|
|
39
|
+
state,
|
|
40
|
+
directive,
|
|
41
|
+
blocks,
|
|
42
|
+
context,
|
|
43
|
+
ui,
|
|
44
|
+
permissions
|
|
45
|
+
} = useBlokkli();
|
|
35
46
|
const selectedEditable = ref(null);
|
|
36
47
|
const hasTransition = ref(false);
|
|
37
48
|
const key = computed(() => {
|
|
@@ -65,6 +76,9 @@ const buildEditable = (fieldName, uuid) => {
|
|
|
65
76
|
if (host.bundle === fromLibraryBlockBundle) {
|
|
66
77
|
return;
|
|
67
78
|
}
|
|
79
|
+
if (host.type === itemEntityType && (!permissions.checkBlockBundlePermission(host.bundle, "edit") || permissions.blockHasRestrictedAncestor(host.uuid))) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
68
82
|
const config = types.editableFieldConfig.forName(
|
|
69
83
|
host.type,
|
|
70
84
|
host.bundle,
|
|
@@ -25,7 +25,10 @@ const { adapter } = defineBlokkliFeature({
|
|
|
25
25
|
requiredAdapterMethods: ["fragmentsAddBlock"],
|
|
26
26
|
dependencies: ["add-list"]
|
|
27
27
|
});
|
|
28
|
-
const { state, $t, types, dom, ui } = useBlokkli();
|
|
28
|
+
const { state, $t, types, dom, ui, permissions } = useBlokkli();
|
|
29
|
+
const canAddFragment = computed(
|
|
30
|
+
() => permissions.checkBlockBundlePermission(fragmentBlockBundle, "add")
|
|
31
|
+
);
|
|
29
32
|
const placedAction = ref(null);
|
|
30
33
|
const onAddFragment = async (name) => {
|
|
31
34
|
if (!placedAction.value || !adapter.fragmentsAddBlock) {
|
|
@@ -44,7 +47,7 @@ const isSupportedOnEntity = computed(
|
|
|
44
47
|
() => types.generallyAvailableBundles.find((v) => v.id === fragmentBlockBundle)
|
|
45
48
|
);
|
|
46
49
|
defineAddAction(() => {
|
|
47
|
-
if (!isSupportedOnEntity.value) {
|
|
50
|
+
if (!isSupportedOnEntity.value || !canAddFragment.value) {
|
|
48
51
|
return;
|
|
49
52
|
}
|
|
50
53
|
return {
|
|
@@ -24,7 +24,8 @@ const {
|
|
|
24
24
|
ui,
|
|
25
25
|
directive,
|
|
26
26
|
blocks,
|
|
27
|
-
fields
|
|
27
|
+
fields,
|
|
28
|
+
permissions
|
|
28
29
|
} = useBlokkli();
|
|
29
30
|
const MAX_RECTS = 11;
|
|
30
31
|
function getDeepestUuid(uuids) {
|
|
@@ -120,7 +121,9 @@ function updateHoverState(mouseX, mouseY, offset, scale, artboardSize) {
|
|
|
120
121
|
height: rect.height / scale
|
|
121
122
|
};
|
|
122
123
|
if (isInsideRect(artboardMouseX, artboardMouseY, blockRect)) {
|
|
123
|
-
|
|
124
|
+
if (!permissions.blockHasRestrictedAncestor(uuid)) {
|
|
125
|
+
hoveredUuids.push(uuid);
|
|
126
|
+
}
|
|
124
127
|
}
|
|
125
128
|
}
|
|
126
129
|
const deepestUuid = getDeepestUuid(hoveredUuids);
|
|
@@ -139,6 +142,10 @@ function updateHoverState(mouseX, mouseY, offset, scale, artboardSize) {
|
|
|
139
142
|
if (!isInsideRect(artboardMouseX, artboardMouseY, editableRect)) continue;
|
|
140
143
|
const key = editableRect.key;
|
|
141
144
|
const entityUuid = key.split(":")[2];
|
|
145
|
+
const block = blocks.getBlock(entityUuid);
|
|
146
|
+
if (block && (!permissions.checkBlockBundlePermission(block.bundle, "edit") || permissions.blockHasRestrictedAncestor(entityUuid))) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
142
149
|
if (deepestUuid && entityUuid === deepestUuid) {
|
|
143
150
|
hoveredEditableFieldRect = editableRect;
|
|
144
151
|
break;
|
|
@@ -187,8 +194,10 @@ function updateHoverState(mouseX, mouseY, offset, scale, artboardSize) {
|
|
|
187
194
|
hoverState.radii[level * 4 + 3] = style.radius[3];
|
|
188
195
|
let type = 0;
|
|
189
196
|
if (isDeepest) {
|
|
190
|
-
const
|
|
191
|
-
if (
|
|
197
|
+
const isRestricted = !permissions.checkBlockBundlePermission(block.bundle, "edit") || !permissions.checkBlockBundlePermission(block.bundle, "delete") || !permissions.checkBlockBundlePermission(block.bundle, "add");
|
|
198
|
+
if (isRestricted) {
|
|
199
|
+
type = 5;
|
|
200
|
+
} else if (state.fromLibraryUuids.value.includes(uuid)) {
|
|
192
201
|
type = 4;
|
|
193
202
|
} else {
|
|
194
203
|
type = style.isInverted ? 3 : 1;
|
|
@@ -223,7 +232,8 @@ const uniforms = computed(() => {
|
|
|
223
232
|
u_color_accent: theme.accent.value[600],
|
|
224
233
|
u_color_teal: theme.teal.value.normal,
|
|
225
234
|
u_color_white: [255, 255, 255],
|
|
226
|
-
u_color_lime: theme.lime.value.normal
|
|
235
|
+
u_color_lime: theme.lime.value.normal,
|
|
236
|
+
u_color_yellow: theme.yellow.value.normal
|
|
227
237
|
};
|
|
228
238
|
});
|
|
229
239
|
onBlokkliEvent("state:reloaded", () => {
|
|
@@ -279,6 +289,7 @@ const { collector } = defineRenderer("hover-overlay", {
|
|
|
279
289
|
u_color_teal: toShaderColor(uniforms.value.u_color_teal),
|
|
280
290
|
u_color_white: toShaderColor(uniforms.value.u_color_white),
|
|
281
291
|
u_color_lime: toShaderColor(uniforms.value.u_color_lime),
|
|
292
|
+
u_color_yellow: toShaderColor(uniforms.value.u_color_yellow),
|
|
282
293
|
u_hover_positions: hoverState.positions,
|
|
283
294
|
u_hover_radii: hoverState.radii,
|
|
284
295
|
u_hover_types: hoverState.types,
|
|
@@ -392,7 +403,9 @@ const { collector } = defineRenderer("hover-overlay", {
|
|
|
392
403
|
ctx2d.stroke();
|
|
393
404
|
} else {
|
|
394
405
|
let strokeColor = colors.u_color_mono;
|
|
395
|
-
if (type ===
|
|
406
|
+
if (type === 5) {
|
|
407
|
+
strokeColor = colors.u_color_yellow;
|
|
408
|
+
} else if (type === 4) {
|
|
396
409
|
strokeColor = colors.u_color_lime;
|
|
397
410
|
} else if (type === 3) {
|
|
398
411
|
strokeColor = colors.u_color_white;
|