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
1 change: 1 addition & 0 deletions Configuration/JavaScriptModules.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
],
'imports' => [
'@web-vision/deepl-write/deeplwrite-plugin.js' => 'EXT:deepl_write/Resources/Public/JavaScript/Ckeditor/deeplwrite-plugin.js',
'@web-vision/deepl-write/readability-progress.js' => 'EXT:deepl_write/Resources/Public/JavaScript/Ckeditor/readability-progress.js',
],
];
71 changes: 2 additions & 69 deletions Resources/Private/Core13/Backend/Templates/CkEditor/Edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,67 +4,6 @@
xmlns:deeplWrite="http://typo3.org/ns/WebVision/DeeplWrite/ViewHelpers"
data-namespace-typo3-fluid="true"
>
<style>
/* CSS */
.progress {
--value: 0; /* 0–100 */

position: relative;
width: 100%;
height: 20px;
border-radius: var(--typo3-input-border-radius);
margin-top: 20px;
margin-bottom: 10px;

/* Full-width rainbow background (not clipped) */
background: linear-gradient(
to right,
#ff0000,
#ff7a00,
#ffd400,
#a8ff00,
#00a400
);
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
overflow: visible;
}

/* Slim vertical marker showing the current percentage */
.progress .marker {
position: absolute;
top: -4px;
bottom: -4px;
width: 2px;
left: calc(var(--value) * 1%);
transform: translateX(-1px);
background: #111;
border-radius: 2px;
box-shadow:
0 0 0 2px #fff,
0 0 0 3px rgba(0,0,0,0.06);
pointer-events: none;
}

/* Percentage label that follows the marker */
.progress .label {
position: absolute;
left: calc(var(--value) * 1%);
top: -22px;
transform: translateX(-50%);
padding: 0 6px;
height: 18px;
display: inline-flex;
align-items: center;
border-radius: 3px;
background: #fff;
border: 1px solid rgba(0,0,0,0.12);
font: 600 12px/1 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
color: #111;
white-space: nowrap;
user-select: none;
pointer-events: none;
}
</style>
<div class="col-md-12">
<div class="row">
<p>
Expand Down Expand Up @@ -152,20 +91,14 @@ <h2 class="visually-hidden">
<label for="original" class="form-label t3js-formengine-label">
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.original.heading"/>
</label>
<div class="progress" id="original-readability" style="--value: 0" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<span class="marker" aria-hidden="true"></span>
<span class="label">0%</span>
</div>
<deepl-write-readability id="original-readability" value="0"></deepl-write-readability>
<textarea rows="10" class="form-control t3js-formengine-textarea formengine-textarea" id="original"></textarea>
</div>
<div class="col-md-6 col-xs-12">
<label for="optimized" class="form-label t3js-formengine-label">
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.optimized.heading"/>
</label>
<div class="progress" id="optimized-readability" style="--value: 0" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<span class="marker" aria-hidden="true"></span>
<span class="label">0%</span>
</div>
<deepl-write-readability id="optimized-readability" value="0"></deepl-write-readability>
<textarea rows="10" class="form-control t3js-formengine-textarea formengine-textarea" id="optimized"></textarea>
</div>
</div>
Expand Down
71 changes: 2 additions & 69 deletions Resources/Private/Core14/Backend/Templates/CkEditor/Edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,67 +4,6 @@
xmlns:deeplWrite="http://typo3.org/ns/WebVision/DeeplWrite/ViewHelpers"
data-namespace-typo3-fluid="true"
>
<style>
/* CSS */
.progress {
--value: 0; /* 0–100 */

position: relative;
width: 100%;
height: 20px;
border-radius: var(--typo3-input-border-radius);
margin-top: 20px;
margin-bottom: 10px;

/* Full-width rainbow background (not clipped) */
background: linear-gradient(
to right,
#ff0000,
#ff7a00,
#ffd400,
#a8ff00,
#00a400
);
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
overflow: visible;
}

/* Slim vertical marker showing the current percentage */
.progress .marker {
position: absolute;
top: -4px;
bottom: -4px;
width: 2px;
left: calc(var(--value) * 1%);
transform: translateX(-1px);
background: #111;
border-radius: 2px;
box-shadow:
0 0 0 2px #fff,
0 0 0 3px rgba(0,0,0,0.06);
pointer-events: none;
}

/* Percentage label that follows the marker */
.progress .label {
position: absolute;
left: calc(var(--value) * 1%);
top: -22px;
transform: translateX(-50%);
padding: 0 6px;
height: 18px;
display: inline-flex;
align-items: center;
border-radius: 3px;
background: #fff;
border: 1px solid rgba(0,0,0,0.12);
font: 600 12px/1 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
color: #111;
white-space: nowrap;
user-select: none;
pointer-events: none;
}
</style>
<div class="col-md-12">
<div class="row">
<p>
Expand Down Expand Up @@ -150,20 +89,14 @@ <h2 class="visually-hidden">
<label for="original" class="form-label t3js-formengine-label">
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.original.heading"/>
</label>
<div class="progress" id="original-readability" style="--value: 0" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<span class="marker" aria-hidden="true"></span>
<span class="label">0%</span>
</div>
<deepl-write-readability id="original-readability" value="0"></deepl-write-readability>
<textarea rows="10" class="form-control t3js-formengine-textarea formengine-textarea" id="original"></textarea>
</div>
<div class="col-md-6 col-xs-12">
<label for="optimized" class="form-label t3js-formengine-label">
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.optimized.heading"/>
</label>
<div class="progress" id="optimized-readability" style="--value: 0" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<span class="marker" aria-hidden="true"></span>
<span class="label">0%</span>
</div>
<deepl-write-readability id="optimized-readability" value="0"></deepl-write-readability>
<textarea rows="10" class="form-control t3js-formengine-textarea formengine-textarea" id="optimized"></textarea>
</div>
</div>
Expand Down
11 changes: 4 additions & 7 deletions Resources/Public/JavaScript/Ckeditor/deeplwrite-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {Plugin} from '@ckeditor/ckeditor5-core';
import {ButtonView} from '@ckeditor/ckeditor5-ui';
import AjaxRequest from '@typo3/core/ajax/ajax-request.js';
import Modal from '@typo3/backend/modal.js';
// Registers the <deepl-write-readability> custom element used in the overlay.
import '@web-vision/deepl-write/readability-progress.js';

export class Deeplwrite extends Plugin {
static pluginName = 'Deeplwrite';
Expand Down Expand Up @@ -119,13 +121,8 @@ export class Deeplwrite extends Plugin {
if (readability.score === null || readability.score === undefined) {
return;
}
const value = Math.max(0, Math.min(100, Number(readability.score) || 0)).toFixed(2);
element.style.setProperty('--value', value);
element.setAttribute('aria-valuenow', String(value));
const label = element.querySelector('.label');
if (label) {
label.textContent = `${value}%`;
}
// The <deepl-write-readability> component clamps, formats and renders.
element.value = Number(readability.score) || 0;
});
}
}
127 changes: 127 additions & 0 deletions Resources/Public/JavaScript/Ckeditor/readability-progress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import {html, LitElement} from 'lit';
import {styleTag} from '@typo3/core/lit-helper.js';

/**
* Readability progress indicator used in the DeepL Write CKEditor overlay.
*
* Renders a full-width rainbow scale with a slim marker and a percentage label
* positioned at the current Flesch reading-ease score (0-100).
*
* Styling lives in the shadow DOM, so it cannot leak into or be influenced by
* other components. Instead of `static styles` (which relies on a constructable
* stylesheet bound to the realm where the module was evaluated), the CSS is
* emitted as a nonce-carrying `<style>` element via TYPO3's `styleTag` helper.
* This is required because `@typo3/backend/modal.js` relocates the overlay
* content into the top-level backend document: a stylesheet constructed in the
* FormEngine iframe realm cannot be adopted into a shadow root owned by the top
* document ("Adopted style sheet's constructor document must match ..."). A
* `<style>` rendered into the template is created in the shadow root's own
* document and carries the `litNonce` of that document, so it is both
* document-safe and Content-Security-Policy compliant (`style-src 'nonce-...'`).
*
* The dynamic position is passed through the `--value` custom property via
* CSSOM (`style.setProperty`), which is not governed by CSP.
*
* Usage:
* <deepl-write-readability value="0"></deepl-write-readability>
* element.value = 42.35; // reactive, re-renders
*/
const componentCss = `
:host {
--value: 0; /* 0-100, overridden per instance via CSSOM */

position: relative;
display: block;
width: 100%;
height: 20px;
margin-top: 20px;
margin-bottom: 10px;
border-radius: var(--typo3-input-border-radius);

/* Full-width rainbow background (not clipped) */
background: linear-gradient(
to right,
#ff0000,
#ff7a00,
#ffd400,
#a8ff00,
#00a400
);
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}

/* Slim vertical marker showing the current percentage */
.marker {
position: absolute;
top: -4px;
bottom: -4px;
left: calc(var(--value) * 1%);
width: 2px;
transform: translateX(-1px);
border-radius: 2px;
background: #111;
box-shadow:
0 0 0 2px #fff,
0 0 0 3px rgba(0, 0, 0, 0.06);
pointer-events: none;
}

/* Percentage label that follows the marker */
.label {
position: absolute;
top: -22px;
left: calc(var(--value) * 1%);
height: 18px;
padding: 0 6px;
transform: translateX(-50%);
display: inline-flex;
align-items: center;
border-radius: 3px;
background: #fff;
border: 1px solid rgba(0, 0, 0, 0.12);
font: 600 12px/1 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
color: #111;
white-space: nowrap;
user-select: none;
pointer-events: none;
}
`;

export class ReadabilityProgress extends LitElement {
static properties = {
value: {type: Number},
};

constructor() {
super();
this.value = 0;
}

render() {
const value = this.clampedValue;
// Use the element's own document window so the `<style>` carries the nonce of
// the document it actually lives in (top document when shown in a modal).
return html`
${styleTag(componentCss, this.ownerDocument.defaultView)}
<span class="marker" aria-hidden="true"></span>
<span class="label">${value.toFixed(2)}%</span>
`;
}

updated() {
const value = this.clampedValue;
// CSSOM write (not a CSP style-src-attr concern) drives marker/label position.
this.style.setProperty('--value', String(value));
// Expose progress semantics on the host element for assistive technology.
this.setAttribute('role', 'progressbar');
this.setAttribute('aria-valuemin', '0');
this.setAttribute('aria-valuemax', '100');
this.setAttribute('aria-valuenow', value.toFixed(2));
}

get clampedValue() {
return Math.max(0, Math.min(100, Number(this.value) || 0));
}
}

customElements.define('deepl-write-readability', ReadabilityProgress);