@inshapardaz/likhari-react 0.1.6-dev.48 → 0.1.6-dev.51

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.
@@ -44,6 +44,15 @@ export interface EditorRootProps {
44
44
  * simple, but they bloat the document (see `images.maxSizeMB`).
45
45
  */
46
46
  onImageUpload?: (file: File) => Promise<string>;
47
+ /**
48
+ * Downloads a linked image's bytes when converting it to an embedded copy
49
+ * or applying a crop/rotate/flip. Without it, the browser fetches the URL
50
+ * directly, which fails for any image server that doesn't send CORS
51
+ * headers — that restriction can't be worked around from the browser
52
+ * alone. Provide this to route the request through your own backend (or a
53
+ * CORS proxy) instead.
54
+ */
55
+ fetchImage?: (url: string) => Promise<Blob>;
47
56
  /**
48
57
  * Entries for the toolbar's font-family dropdown. Defaults to
49
58
  * `DEFAULT_FONT_OPTIONS` (generic Latin faces plus the Urdu/Arabic-script
@@ -1 +1 @@
1
- {"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAS1D,OAAO,KAAK,EAAe,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAIjE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,YAAY,CAAC,CAAC;IACzD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,cAAc,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;IACrD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClD,iBAAiB,IAAI,OAAO,CAAC;IAC7B,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAQD,eAAO,MAAM,UAAU,uGA2IrB,CAAC"}
1
+ {"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAS1D,OAAO,KAAK,EAAe,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAIjE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,YAAY,CAAC,CAAC;IACzD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,cAAc,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;IACrD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClD,iBAAiB,IAAI,OAAO,CAAC;IAC7B,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAQD,eAAO,MAAM,UAAU,uGA6IrB,CAAC"}
@@ -23,7 +23,7 @@ function initialEditorStateJson(initialContent) {
23
23
  const state = defaultFormatRegistry.parse(initialContent.format, initialContent.value);
24
24
  return JSON.stringify(state);
25
25
  }
26
- export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialContent, featureConfig, featurePreset, theme, colorScheme, locale = 'en', placeholder = 'Start writing…', height = '480px', onChange, onSave, showSave = Boolean(onSave), onImageUpload, fontOptions, }, ref) {
26
+ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialContent, featureConfig, featurePreset, theme, colorScheme, locale = 'en', placeholder = 'Start writing…', height = '480px', onChange, onSave, showSave = Boolean(onSave), onImageUpload, fetchImage, fontOptions, }, ref) {
27
27
  const config = useMemo(() => resolveFeatureConfig(featureConfig, featurePreset), [featureConfig, featurePreset]);
28
28
  // The urdu-web-fonts stylesheets are needed for the font dropdown and for
29
29
  // RTL content, whose canvas font (editor.css) is one of those families.
@@ -37,7 +37,8 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
37
37
  allowCaption: config.images.caption,
38
38
  maxSizeMB: config.images.maxSizeMB,
39
39
  onImageUpload,
40
- }), [config.images.linked, config.images.embedded, config.images.caption, config.images.maxSizeMB, onImageUpload]);
40
+ fetchImage,
41
+ }), [config.images.linked, config.images.embedded, config.images.caption, config.images.maxSizeMB, onImageUpload, fetchImage]);
41
42
  const editorStateRef = useRef(null);
42
43
  const lastSavedJsonRef = useRef(null);
43
44
  const [isDirty, setIsDirty] = useState(false);
@@ -48,7 +48,7 @@ function useNaturalSize(src) {
48
48
  * `onImageUpload` when there is one.
49
49
  */
50
50
  export function ImageDialog({ mode, opened, initial, intent, onSubmit, onClose }) {
51
- const { allowLinked, allowEmbedded, allowCaption, maxSizeMB, onImageUpload } = useImageOptions();
51
+ const { allowLinked, allowEmbedded, allowCaption, maxSizeMB, onImageUpload, fetchImage } = useImageOptions();
52
52
  const editing = mode === 'edit' && initial !== undefined;
53
53
  const defaultSource = editing ? 'keep' : allowLinked ? 'linked' : 'embedded';
54
54
  const [tab, setTab] = useState('details');
@@ -136,7 +136,7 @@ export function ImageDialog({ mode, opened, initial, intent, onSubmit, onClose }
136
136
  setConverting(true);
137
137
  setError(null);
138
138
  try {
139
- const dataUrl = await fetchImageAsDataUrl(src);
139
+ const dataUrl = await fetchImageAsDataUrl(src, fetchImage);
140
140
  if (dataUrlBytes(dataUrl) > maxSizeMB * 1024 * 1024)
141
141
  throw new Error(`Image is larger than ${maxSizeMB} MB`);
142
142
  setSource('embedded');
@@ -182,7 +182,7 @@ export function ImageDialog({ mode, opened, initial, intent, onSubmit, onClose }
182
182
  let newPixels = pixelSrc ?? (source === 'embedded' ? fileSrc : null);
183
183
  if (!newPixels && source === 'linked' && embedCopy) {
184
184
  setBusy(true);
185
- newPixels = await fetchImageAsDataUrl(effectiveSrc);
185
+ newPixels = await fetchImageAsDataUrl(effectiveSrc, fetchImage);
186
186
  }
187
187
  if (newPixels) {
188
188
  if (dataUrlBytes(newPixels) > maxSizeMB * 1024 * 1024) {
@@ -11,6 +11,13 @@ export interface ImageOptions {
11
11
  maxSizeMB: number;
12
12
  /** Host upload handler; without it, uploads are embedded as base64 data URIs. */
13
13
  onImageUpload?: (file: File) => Promise<string>;
14
+ /** Host override for downloading a linked image's bytes, used when converting
15
+ * a link to an embedded image or applying a crop/rotate/flip. Without it, the
16
+ * browser fetches the URL directly, which fails for any server that doesn't
17
+ * send CORS headers. A host can provide this to route the request through
18
+ * its own backend (or a CORS proxy) instead, since that restriction can only
19
+ * be worked around server-side, never from the browser alone. */
20
+ fetchImage?: (url: string) => Promise<Blob>;
14
21
  }
15
22
  export declare const ImageOptionsContext: import("react").Context<ImageOptions>;
16
23
  export declare function useImageOptions(): ImageOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"ImageOptionsContext.d.ts","sourceRoot":"","sources":["../../src/image/ImageOptionsContext.tsx"],"names":[],"mappings":"AAEA;kFACkF;AAClF,MAAM,WAAW,YAAY;IAC3B,8DAA8D;IAC9D,WAAW,EAAE,OAAO,CAAC;IACrB,kDAAkD;IAClD,aAAa,EAAE,OAAO,CAAC;IACvB,oDAAoD;IACpD,YAAY,EAAE,OAAO,CAAC;IACtB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACjD;AAED,eAAO,MAAM,mBAAmB,uCAK9B,CAAC;AAEH,wBAAgB,eAAe,IAAI,YAAY,CAE9C"}
1
+ {"version":3,"file":"ImageOptionsContext.d.ts","sourceRoot":"","sources":["../../src/image/ImageOptionsContext.tsx"],"names":[],"mappings":"AAEA;kFACkF;AAClF,MAAM,WAAW,YAAY;IAC3B,8DAA8D;IAC9D,WAAW,EAAE,OAAO,CAAC;IACrB,kDAAkD;IAClD,aAAa,EAAE,OAAO,CAAC;IACvB,oDAAoD;IACpD,YAAY,EAAE,OAAO,CAAC;IACtB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;qEAKiE;IACjE,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAED,eAAO,MAAM,mBAAmB,uCAK9B,CAAC;AAEH,wBAAgB,eAAe,IAAI,YAAY,CAE9C"}
@@ -26,8 +26,10 @@ export declare function dataUrlBytes(dataUrl: string): number;
26
26
  export declare function dataUrlToFile(dataUrl: string, name: string): File;
27
27
  export declare const CANNOT_DOWNLOAD = "Couldn't download this image (its server doesn't allow it). Download the file and upload it instead.";
28
28
  /** Downloads an image and returns it as a base64 data URI, to embed it in the
29
- * document. Needs the image's server to allow cross-origin reads (CORS). */
30
- export declare function fetchImageAsDataUrl(url: string, fetchImpl?: typeof fetch): Promise<string>;
29
+ * document. Without a `fetchBlob` override, needs the image's server to allow
30
+ * cross-origin reads (CORS) — a host can pass one to route the download
31
+ * through its own backend or a CORS proxy instead. */
32
+ export declare function fetchImageAsDataUrl(url: string, fetchBlob?: (url: string) => Promise<Blob>): Promise<string>;
31
33
  export type PixelOp = {
32
34
  type: 'rotate';
33
35
  direction: 'cw' | 'ccw';
@@ -1 +1 @@
1
- {"version":3,"file":"imageEdit.d.ts","sourceRoot":"","sources":["../../src/image/imageEdit.ts"],"names":[],"mappings":"AAEA,2EAA2E;AAC3E,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;sEACsE;AACtE,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;EAMxE;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,GAAG,QAAQ,EAC3B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,GACpB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAKnC;AAED;mFACmF;AACnF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C;AASD,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKpD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAOjE;AAED,eAAO,MAAM,eAAe,yGAC4E,CAAC;AAEzG;4EAC4E;AAC5E,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBvG;AAED,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,IAAI,GAAG,KAAK,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,GAAG,UAAU,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAErC;+EAC+E;AAC/E,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAQhE;AAED,eAAO,MAAM,kBAAkB,iHACiF,CAAC;AAEjH,uFAAuF;AACvF,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAuC5E"}
1
+ {"version":3,"file":"imageEdit.d.ts","sourceRoot":"","sources":["../../src/image/imageEdit.ts"],"names":[],"mappings":"AAEA,2EAA2E;AAC3E,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;sEACsE;AACtE,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;EAMxE;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,GAAG,QAAQ,EAC3B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,GACpB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAKnC;AAED;mFACmF;AACnF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C;AASD,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKpD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAOjE;AAED,eAAO,MAAM,eAAe,yGAC4E,CAAC;AAEzG;;;sDAGsD;AACtD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAIpC,GACH,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,IAAI,GAAG,KAAK,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,GAAG,UAAU,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAErC;+EAC+E;AAC/E,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAQhE;AAED,eAAO,MAAM,kBAAkB,iHACiF,CAAC;AAEjH,uFAAuF;AACvF,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAuC5E"}
@@ -47,16 +47,19 @@ export function dataUrlToFile(dataUrl, name) {
47
47
  }
48
48
  export const CANNOT_DOWNLOAD = "Couldn't download this image (its server doesn't allow it). Download the file and upload it instead.";
49
49
  /** Downloads an image and returns it as a base64 data URI, to embed it in the
50
- * document. Needs the image's server to allow cross-origin reads (CORS). */
51
- export async function fetchImageAsDataUrl(url, fetchImpl = fetch) {
50
+ * document. Without a `fetchBlob` override, needs the image's server to allow
51
+ * cross-origin reads (CORS) — a host can pass one to route the download
52
+ * through its own backend or a CORS proxy instead. */
53
+ export async function fetchImageAsDataUrl(url, fetchBlob = (u) => fetch(u, { mode: 'cors' }).then((response) => {
54
+ if (!response.ok)
55
+ throw new Error('bad status');
56
+ return response.blob();
57
+ })) {
52
58
  if (/^data:image\//i.test(url))
53
59
  return url;
54
60
  let blob;
55
61
  try {
56
- const response = await fetchImpl(url, { mode: 'cors' });
57
- if (!response.ok)
58
- throw new Error('bad status');
59
- blob = await response.blob();
62
+ blob = await fetchBlob(url);
60
63
  }
61
64
  catch {
62
65
  throw new Error(CANNOT_DOWNLOAD);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.6-dev.48",
3
+ "version": "0.1.6-dev.51",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {