@37signals/lexxy 0.9.15-alpha.1 → 0.9.15-alpha.2

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.
Files changed (2) hide show
  1. package/dist/lexxy.esm.js +49 -20
  2. package/package.json +1 -1
package/dist/lexxy.esm.js CHANGED
@@ -1968,6 +1968,11 @@ function safeCloneEditorState(editorState) {
1968
1968
  return clone
1969
1969
  }
1970
1970
 
1971
+ const INITIAL_PREVIEW_POLL_DELAY_MS = 3000;
1972
+ const MAX_PREVIEW_POLL_DELAY_MS = 120000;
1973
+ const MAX_PREVIEW_POLL_ATTEMPTS = 20;
1974
+
1975
+
1971
1976
  class ActionTextAttachmentNode extends DecoratorNode {
1972
1977
  static getType() {
1973
1978
  return "action_text_attachment"
@@ -2042,7 +2047,7 @@ class ActionTextAttachmentNode extends DecoratorNode {
2042
2047
  return Lexxy.global.get("attachmentTagName")
2043
2048
  }
2044
2049
 
2045
- constructor({ tagName, sgid, src, previewSrc, previewable, pendingPreview, altText, caption, contentType, fileName, fileSize, width, height, uploadError }, key) {
2050
+ constructor({ tagName, sgid, src, previewSrc, previewable, previewStatusUrl, pendingPreview, altText, caption, contentType, fileName, fileSize, width, height, uploadError }, key) {
2046
2051
  super(key);
2047
2052
 
2048
2053
  this.tagName = tagName || ActionTextAttachmentNode.TAG_NAME;
@@ -2050,6 +2055,7 @@ class ActionTextAttachmentNode extends DecoratorNode {
2050
2055
  this.src = src;
2051
2056
  this.previewSrc = previewSrc;
2052
2057
  this.previewable = parseBoolean(previewable);
2058
+ this.previewStatusUrl = previewStatusUrl;
2053
2059
  this.pendingPreview = pendingPreview;
2054
2060
  this.altText = altText || "";
2055
2061
  this.caption = caption || "";
@@ -2128,6 +2134,8 @@ class ActionTextAttachmentNode extends DecoratorNode {
2128
2134
  sgid: this.sgid,
2129
2135
  src: this.src,
2130
2136
  previewable: this.previewable,
2137
+ previewStatusUrl: this.previewStatusUrl,
2138
+ pendingPreview: this.pendingPreview,
2131
2139
  altText: this.altText,
2132
2140
  caption: this.caption,
2133
2141
  contentType: this.contentType,
@@ -2246,41 +2254,61 @@ class ActionTextAttachmentNode extends DecoratorNode {
2246
2254
  });
2247
2255
  }
2248
2256
 
2257
+ // While the file-icon is shown, watch for the preview to become ready.
2258
+ // With a status URL, poll it (2xx = processing, anything else = ready).
2259
+ // Without one, preload the preview URL once and swap on load.
2249
2260
  #pollForPreview(figure) {
2261
+ if (this.previewStatusUrl) {
2262
+ this.#waitForPreviewByPollingStatus(figure);
2263
+ } else {
2264
+ this.#waitForPreviewByPreloadingImage(figure);
2265
+ }
2266
+ }
2267
+
2268
+ #waitForPreviewByPollingStatus(figure) {
2250
2269
  let attempt = 0;
2251
- const maxAttempts = 10;
2252
2270
 
2253
- const tryLoad = () => {
2271
+ const tryStatus = async () => {
2254
2272
  if (!this.editor.read(() => this.isAttached())) return
2255
2273
 
2256
- const img = new Image();
2257
- const cacheBustedSrc = `${this.src}${this.src.includes("?") ? "&" : "?"}_=${Date.now()}`;
2274
+ try {
2275
+ // redirect: "manual" prevents fetch from transparently following a
2276
+ // 3xx response — without it, a status endpoint that redirected to,
2277
+ // say, the preview URL would resolve to a 200 and look like
2278
+ // "still processing." The contract is "any non-2xx means done."
2279
+ const response = await fetch(this.previewStatusUrl, { credentials: "include", redirect: "manual" });
2258
2280
 
2259
- img.onload = () => {
2260
2281
  if (!this.editor.read(() => this.isAttached())) return
2261
2282
 
2262
- // The placeholder is a file-type icon SVG (86×100). A real thumbnail
2263
- // generated from PDF/video content is significantly larger.
2264
- if (img.naturalWidth > 150 && img.naturalHeight > 150) {
2265
- this.#swapToPreviewDOM(figure, cacheBustedSrc);
2266
- } else {
2283
+ if (response.ok) {
2267
2284
  retry();
2285
+ } else {
2286
+ this.#swapToPreviewDOM(figure, this.src);
2268
2287
  }
2269
- };
2270
- img.onerror = () => retry();
2271
- img.src = cacheBustedSrc;
2288
+ } catch {
2289
+ retry();
2290
+ }
2272
2291
  };
2273
2292
 
2274
2293
  const retry = () => {
2275
2294
  attempt++;
2276
- if (attempt < maxAttempts && this.editor.read(() => this.isAttached())) {
2277
- const delay = Math.min(2000 * Math.pow(1.5, attempt), 15000);
2278
- setTimeout(tryLoad, delay);
2295
+ if (attempt < MAX_PREVIEW_POLL_ATTEMPTS && this.editor.read(() => this.isAttached())) {
2296
+ const delay = Math.min(2000 * Math.pow(1.5, attempt), MAX_PREVIEW_POLL_DELAY_MS);
2297
+ setTimeout(tryStatus, delay);
2279
2298
  }
2280
2299
  };
2281
2300
 
2282
2301
  // Give the server time to start processing before the first attempt
2283
- setTimeout(tryLoad, 3000);
2302
+ setTimeout(tryStatus, INITIAL_PREVIEW_POLL_DELAY_MS);
2303
+ }
2304
+
2305
+ #waitForPreviewByPreloadingImage(figure) {
2306
+ const img = new Image();
2307
+ img.onload = () => {
2308
+ if (!this.editor.read(() => this.isAttached())) return
2309
+ this.#swapToPreviewDOM(figure, this.src);
2310
+ };
2311
+ img.src = this.src;
2284
2312
  }
2285
2313
 
2286
2314
  #swapToPreviewDOM(figure, previewSrc) {
@@ -4859,7 +4887,7 @@ class ImageGalleryNode extends ElementNode {
4859
4887
  replaceWithSingularChild() {
4860
4888
  if (this.#hasSingularChild) {
4861
4889
  const child = this.getFirstChild();
4862
- return this.replace(child)
4890
+ return this.replace($makeSafeForRoot(child))
4863
4891
  }
4864
4892
  }
4865
4893
 
@@ -5287,6 +5315,7 @@ class AttachmentNodeConversion {
5287
5315
  fileName: blob.filename,
5288
5316
  fileSize: blob.byte_size,
5289
5317
  previewable: blob.previewable,
5318
+ previewStatusUrl: blob.preview_status_url
5290
5319
  }
5291
5320
  }
5292
5321
 
@@ -8866,7 +8895,7 @@ class LexicalPromptElement extends HTMLElement {
8866
8895
 
8867
8896
  const popoverRect = this.popoverElement.getBoundingClientRect();
8868
8897
 
8869
- if (popoverRect.right > window.innerWidth) {
8898
+ if (popoverRect.right > editorRect.right) {
8870
8899
  this.popoverElement.toggleAttribute("data-clipped-at-right", true);
8871
8900
  }
8872
8901
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@37signals/lexxy",
3
- "version": "0.9.15-alpha.1",
3
+ "version": "0.9.15-alpha.2",
4
4
  "description": "Lexxy - A modern rich text editor for Rails.",
5
5
  "module": "dist/lexxy.esm.js",
6
6
  "type": "module",