mnfst 0.5.231 → 0.5.232

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.
@@ -484,6 +484,8 @@ function installMpaStickyLocaleLinks() {
484
484
 
485
485
  const link = event.target.closest('a');
486
486
  if (!link || link.closest('[data-manifest-skip-locale-sticky]')) return;
487
+ const editable = link.closest('[contenteditable]');
488
+ if (link.isContentEditable || (editable && editable.getAttribute('contenteditable') !== 'false')) return; // content in editable text
487
489
  if (link.hasAttribute('download')) return;
488
490
 
489
491
  const hrefAttr = link.getAttribute('href');
@@ -526,9 +528,15 @@ function installMpaStickyLocaleLinks() {
526
528
  function interceptLinkClicks() {
527
529
  // Use capture phase to intercept before other handlers
528
530
  document.addEventListener('click', (event) => {
531
+ if (event.defaultPrevented) return; // an editor (or the page) owns this click
529
532
  const link = event.target.closest('a');
530
533
  if (!link) return;
531
534
 
535
+ // A link inside editable text (x-rich-text, x-edit page text) is content: the click
536
+ // places the caret, as the browser does natively, and never navigates
537
+ const host = link.closest('[contenteditable]');
538
+ if (link.isContentEditable || (host && host.getAttribute('contenteditable') !== 'false')) return;
539
+
532
540
  const href = link.getAttribute('href');
533
541
  if (!href || href.startsWith('mailto:') || href.startsWith('tel:')) return;
534
542
 
@@ -16,7 +16,7 @@
16
16
 
17
17
  // Build stamps the published version here; unstamped (dev) falls back to the
18
18
  // registration URL's `v`, then 'dev'.
19
- const BUILD_VERSION = '0.5.231';
19
+ const BUILD_VERSION = '0.5.232';
20
20
  const CACHE_PREFIX = 'mnfst-sw:';
21
21
  const META_CACHE = CACHE_PREFIX + 'meta';
22
22
  // Sized to hold a full precache.json warm-up (PRECACHE_MAX_ENTRIES server-side)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst",
3
- "version": "0.5.231",
3
+ "version": "0.5.232",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",
@@ -57,6 +57,7 @@
57
57
  "@tailwindcss/cli": "^4.2.4",
58
58
  "alpinejs": "^3.17.1",
59
59
  "cssnano": "^7.1.1",
60
+ "entities": "4.5.0",
60
61
  "glob": "^11.0.2",
61
62
  "happy-dom": "^20.11.6",
62
63
  "puppeteer": "^24.15.0",
@@ -1,273 +0,0 @@
1
- /* Manifest Text Edit */
2
-
3
- @layer base, components, utilities;
4
-
5
- /* Self-contained. The editable area is a field first — it borrows the tokens inputs
6
- and textareas use, so it sits in a form without looking bolted on. Controls are
7
- styled where they are, not by where they sit, because they can be anywhere. */
8
-
9
- @layer components {
10
-
11
- :where([data-text-edit]) {
12
- position: relative;
13
- overflow-y: auto;
14
- max-height: var(--text-edit-max-height, none);
15
- outline: none;
16
-
17
- /* Page-level styling — set by .page controls, read back by $text.page.
18
- Presentation, so it lives here rather than inside the stored markup. */
19
- font-family: var(--text-edit-font, inherit);
20
- font-size: var(--text-edit-size, inherit);
21
- line-height: var(--text-edit-leading, inherit);
22
- text-align: var(--text-edit-align, start);
23
- color: var(--text-edit-color, inherit);
24
-
25
- /* Flagged from script: :empty never matches the <br> a contenteditable
26
- keeps, and the tag holding it is not predictable after a wipe. The
27
- placeholder OVERLAYS the first line (it is not a line of its own), so
28
- the caret sits on top of it and nothing jumps when typing starts. */
29
- &[data-text-edit-empty]::before {
30
- content: attr(placeholder);
31
- position: absolute;
32
- inset-block-start: var(--text-edit-padding, 0.75rem);
33
- inset-inline: var(--text-edit-padding, 0.75rem);
34
- color: var(--color-content-subtle);
35
- pointer-events: none;
36
- }
37
- }
38
-
39
- /* A .page background applies to an unstyled area too. */
40
- :where([data-text-edit].unstyle[style*="--text-edit-background"]) {
41
- background: var(--text-edit-background);
42
- }
43
-
44
- /* An unstyled area has no field padding, so its placeholder hugs the line. */
45
- :where([data-text-edit].unstyle)[data-text-edit-empty]::before {
46
- inset-block-start: 0;
47
- inset-inline: 0;
48
- }
49
-
50
- /* The FIELD look. `.unstyle` drops it so an area can sit seamlessly in a
51
- page — a WYSIWYG document inherits its element's own type and colours;
52
- everything above (caret custody, placeholder, commands) still works. */
53
- :where([data-text-edit]:not(.unstyle)) {
54
- min-height: var(--text-edit-min-height, 8rem);
55
- padding: var(--text-edit-padding, 0.75rem);
56
- background: var(--text-edit-background, var(--color-surface-1));
57
- border: 1px solid var(--color-line);
58
- border-radius: var(--radius);
59
-
60
- /* Held while the area owns the editing session — a <select> or colour
61
- control takes NATIVE focus by design, and the ring must not flicker
62
- off while the writer is mid-command. */
63
- &:focus,
64
- &[data-text-edit-focused] {
65
- border-color: var(--color-content-subtle);
66
- }
67
- }
68
-
69
- /* ---- Selection menu (x-text-edit-menu) ----
70
- Positioned by the --text-edit-selection-* variables the plugin writes to
71
- :root, so the default needs no script; restyle or reposition freely. */
72
- :where([data-text-edit-menu]) {
73
- position: fixed;
74
- margin: 0;
75
- inset: auto;
76
- left: var(--text-edit-selection-center);
77
- top: var(--text-edit-selection-y);
78
- transform: translate(-50%, calc(-100% - 0.5rem));
79
- gap: 0.25rem;
80
- align-items: center;
81
- padding: 0.25rem;
82
- background: var(--color-surface-1);
83
- border: 1px solid var(--color-line);
84
- border-radius: var(--radius);
85
- box-shadow: 0 8px 30px rgb(0 0 0 / 0.2);
86
- }
87
-
88
- /* display only while OPEN: an author display on the base rule would beat
89
- the UA's closed-popover display:none, leaving the menu floating fixed
90
- over the page before any selection exists. */
91
- :where([data-text-edit-menu]:popover-open) {
92
- display: flex;
93
- }
94
-
95
- /* Without the Popover API the attribute is inert and the menu would render
96
- permanently fixed on the page — hide it there; the bubble is progressive
97
- enhancement. (In supporting browsers a closed popover is hidden natively.) */
98
- @supports not selector(:popover-open) {
99
- :where([data-text-edit-menu]) {
100
- display: none;
101
- }
102
- }
103
-
104
- /* ---- Controls ----
105
- A control is marked wherever it lives, so a toolbar is optional markup and
106
- never a requirement. The reset already dims [aria-disabled=true]. */
107
- :where([data-text-edit-control]) {
108
- &[data-text-edit-active] {
109
- background: color-mix(in oklch, var(--color-content-stark) 10%, transparent);
110
- color: var(--color-content-stark);
111
- }
112
-
113
- & > svg {
114
- width: 1rem;
115
- height: 1rem;
116
- }
117
- }
118
-
119
- /* Icon-only buttons; text buttons keep the house padding. */
120
- :where(button[data-text-edit-control]:has(> svg:only-child)) {
121
- padding: 0.35rem;
122
- line-height: 0;
123
- }
124
-
125
- /* The reset drops pointer events on disabled elements; a click would fall
126
- through and blur the area. The plugin ignores it instead. */
127
- :where([data-text-edit-control][aria-disabled=true]) {
128
- pointer-events: auto;
129
- }
130
-
131
- /* ---- Editable content ----
132
- The editor authors real block elements, so style them as elements. Deliberately
133
- lighter than the .prose class, which sets a reading measure this field can't use. */
134
- :where([data-text-edit]) {
135
-
136
- & > * + * {
137
- margin-block-start: 0.75em;
138
- }
139
-
140
- & :where(h1, h2, h3, h4, h5, h6) {
141
- font-weight: 600;
142
- line-height: 1.25;
143
- }
144
-
145
- & h1 { font-size: 1.6em }
146
- & h2 { font-size: 1.35em }
147
- & h3 { font-size: 1.15em }
148
-
149
- & :where(ul, ol) {
150
- padding-inline-start: 1.5em;
151
- }
152
-
153
- & li {
154
- list-style: revert;
155
- }
156
-
157
- & blockquote {
158
- padding-inline-start: 0.85em;
159
- border-inline-start: 2px solid var(--color-line);
160
- color: var(--color-content-neutral);
161
- }
162
-
163
- & code {
164
- padding: 0.1em 0.3em;
165
- border-radius: 4px;
166
- font-size: 0.9em;
167
- background: color-mix(in oklch, var(--color-content-stark) 8%, transparent);
168
- }
169
-
170
- & pre {
171
- padding: 0.75em;
172
- border-radius: var(--radius);
173
- overflow-x: auto;
174
- background: color-mix(in oklch, var(--color-content-stark) 8%, transparent);
175
-
176
- & code {
177
- padding: 0;
178
- background: none;
179
- }
180
- }
181
-
182
- & a {
183
- color: var(--color-brand-content);
184
- text-decoration: underline;
185
- }
186
-
187
- & hr {
188
- margin-block: 1em;
189
- }
190
-
191
- & img {
192
- max-width: 100%;
193
- }
194
-
195
- & table {
196
- width: 100%;
197
- table-layout: fixed;
198
-
199
- & :where(td, th) {
200
- padding: 0.35em 0.5em;
201
- border: 1px solid var(--color-line);
202
- vertical-align: top;
203
- }
204
-
205
- & th {
206
- font-weight: 600;
207
- text-align: start;
208
- background: var(--color-surface-2);
209
- }
210
- }
211
-
212
- /* A block selection across cells. The browser can only highlight text, so an
213
- empty cell shows nothing — this is what makes a spreadsheet-style
214
- selection visible. */
215
- & :where(td, th)[data-text-edit-cell] {
216
- background: color-mix(in oklch, var(--color-brand-content) 18%, transparent);
217
- }
218
-
219
- /* Borders read as grabbable only when the pointer is on one */
220
- &[data-text-edit-border="x"] { cursor: col-resize }
221
- &[data-text-edit-border="y"] { cursor: row-resize }
222
- &[data-text-edit-resizing] { user-select: none }
223
- &[data-text-edit-resizing="x"] { cursor: col-resize }
224
- &[data-text-edit-resizing="y"] { cursor: row-resize }
225
-
226
- /* Task lists — the checkbox replaces the marker */
227
- & :where(ul[data-checklist]) {
228
- padding-inline-start: 0.25em;
229
- list-style: none;
230
-
231
- & li {
232
- display: flex;
233
- align-items: baseline;
234
- gap: 0.5em;
235
- list-style: none;
236
- }
237
- }
238
-
239
- & :where(dt) {
240
- font-weight: 600;
241
- }
242
-
243
- & :where(dd) {
244
- margin-inline-start: 1.5em;
245
- }
246
-
247
- & :where(kbd) {
248
- padding: 0.1em 0.35em;
249
- border: 1px solid var(--color-line);
250
- border-radius: 4px;
251
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
252
- font-size: 0.85em;
253
- }
254
-
255
- & :where(mark) {
256
- background: color-mix(in oklch, var(--color-brand-content) 25%, transparent);
257
- color: inherit;
258
- }
259
-
260
- & :where(small) {
261
- font-size: 0.8em;
262
- }
263
-
264
- & :where(figcaption) {
265
- font-size: 0.8em;
266
- color: var(--color-content-neutral);
267
- }
268
-
269
- & :where(abbr, dfn) {
270
- text-decoration: underline dotted;
271
- }
272
- }
273
- }