@harbour-enterprises/superdoc 0.20.0-next.6 → 0.20.0-next.7

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.
@@ -1,5 +1,5 @@
1
1
  import { m as defineComponent, B as h, O as Transition, $ as process$1, I as watchEffect, d as computed, r as ref, j as onMounted, W as onUnmounted, c as createElementBlock, o as openBlock, a as createBaseVNode, f as createCommentVNode, v as createVNode, x as unref } from "./vue-CXxsqYcP.es.js";
2
- import { d as derived, c, a as cB, f as fadeInTransition, b as cM, N as NBaseLoading, w as warnOnce, u as useConfig, e as useTheme, p as pxfy, g as createKey, h as useThemeClass, i as useCompitable, _ as _export_sfc, j as useSuperdocStore, s as storeToRefs, k as useSelection } from "./index-Cl1u_lKk.es.js";
2
+ import { d as derived, c, a as cB, f as fadeInTransition, b as cM, N as NBaseLoading, w as warnOnce, u as useConfig, e as useTheme, p as pxfy, g as createKey, h as useThemeClass, i as useCompitable, _ as _export_sfc, j as useSuperdocStore, s as storeToRefs, k as useSelection } from "./index-CqLcgYHG.es.js";
3
3
  function self(vars) {
4
4
  const {
5
5
  opacityDisabled,
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const vue = require("./vue-DWle4Cai.cjs");
4
- const superdoc = require("./index-CuHtS7O9.cjs");
4
+ const superdoc = require("./index-CMBPzhIR.cjs");
5
5
  function self(vars) {
6
6
  const {
7
7
  opacityDisabled,
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const superEditor_es = require("./super-editor.es-BIW7iKAk.cjs");
2
+ const superEditor_es = require("./super-editor.es-5O71lyiI.cjs");
3
3
  const vue = require("./vue-DWle4Cai.cjs");
4
4
  const jszip = require("./jszip-b7l8QkfH.cjs");
5
5
  const blankDocx = require("./blank-docx-CPqX9RF5.cjs");
@@ -17386,7 +17386,7 @@ const _sfc_main = {
17386
17386
  __name: "SuperDoc",
17387
17387
  emits: ["selection-update"],
17388
17388
  setup(__props, { emit: __emit }) {
17389
- const PdfViewer = vue.defineAsyncComponent(() => Promise.resolve().then(() => require("./PdfViewer--YKlzafo.cjs")));
17389
+ const PdfViewer = vue.defineAsyncComponent(() => Promise.resolve().then(() => require("./PdfViewer-DXtnxGoU.cjs")));
17390
17390
  const superdocStore = useSuperdocStore();
17391
17391
  const commentsStore = useCommentsStore();
17392
17392
  const {
@@ -1,4 +1,4 @@
1
- import { q as index$1, C as CommentsPluginKey, h as TrackChangesBasePluginKey, E as Editor, n as getRichTextExtensions, f as SuperInput, e as SuperEditor, A as AIWriter, g as SuperToolbar, i as createZip } from "./super-editor.es-DN4v75aq.es.js";
1
+ import { q as index$1, C as CommentsPluginKey, h as TrackChangesBasePluginKey, E as Editor, n as getRichTextExtensions, f as SuperInput, e as SuperEditor, A as AIWriter, g as SuperToolbar, i as createZip } from "./super-editor.es-BTalhwn-.es.js";
2
2
  import { a0 as effectScope, r as ref, _ as markRaw, $ as process$1, a1 as toRaw, d as computed, a2 as isRef, a3 as isReactive, C as toRef, i as inject, p as getCurrentInstance, l as watch, x as unref, a4 as hasInjectionContext, M as reactive, s as nextTick, a5 as getCurrentScope, a6 as onScopeDispose, a7 as toRefs, g as global$1, J as shallowRef, N as readonly, j as onMounted, k as onBeforeUnmount, h as onBeforeMount, S as onActivated, q as onDeactivated, z as createTextVNode, F as Fragment, Q as Comment, m as defineComponent, D as provide, H as withDirectives, B as h, U as Teleport, R as renderSlot, V as isVNode, I as watchEffect, O as Transition, a8 as TransitionGroup, E as mergeProps, P as vShow, G as cloneVNode, T as Text, c as createElementBlock, o as openBlock, t as toDisplayString, v as createVNode, y as withCtx, a as createBaseVNode, A as normalizeStyle, f as createCommentVNode, u as createBlock, w as withModifiers, n as normalizeClass, a9 as resolveDirective, e as renderList, b as createApp, X as resolveDynamicComponent, aa as defineAsyncComponent } from "./vue-CXxsqYcP.es.js";
3
3
  import { B as Buffer$2 } from "./jszip-B8KIZSNe.es.js";
4
4
  import { B as BlankDOCX } from "./blank-docx-iwdyG9RH.es.js";
@@ -17369,7 +17369,7 @@ const _sfc_main = {
17369
17369
  __name: "SuperDoc",
17370
17370
  emits: ["selection-update"],
17371
17371
  setup(__props, { emit: __emit }) {
17372
- const PdfViewer = defineAsyncComponent(() => import("./PdfViewer-CuiUv0d5.es.js"));
17372
+ const PdfViewer = defineAsyncComponent(() => import("./PdfViewer-BYyZViQt.es.js"));
17373
17373
  const superdocStore = useSuperdocStore();
17374
17374
  const commentsStore = useCommentsStore();
17375
17375
  const {
@@ -58836,6 +58836,57 @@ function multiStepResize(canvas, width, height) {
58836
58836
  }
58837
58837
  resample_high_quality(canvas, width, height);
58838
58838
  }
58839
+ const FALLBACK_NAME = "image";
58840
+ const stripDiacritics = (value) => value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
58841
+ const sanitizeSegment = (segment, { allowDots = false } = {}) => {
58842
+ if (!segment) return "";
58843
+ const normalized = stripDiacritics(segment).replace(/[\s\u2000-\u206f]+/g, "_").replace(/[\\/]+/g, "_");
58844
+ const allowedPattern = allowDots ? /[^0-9A-Za-z._-]+/g : /[^0-9A-Za-z_-]+/g;
58845
+ let sanitized = normalized.replace(allowedPattern, "_");
58846
+ sanitized = sanitized.replace(/_+/g, "_");
58847
+ sanitized = sanitized.replace(/^[_.-]+/, "");
58848
+ sanitized = sanitized.replace(/[_-]+$/, "");
58849
+ return sanitized;
58850
+ };
58851
+ const splitFileName = (name) => {
58852
+ const trimmed = name?.trim?.() ?? "";
58853
+ const lastDot = trimmed.lastIndexOf(".");
58854
+ if (lastDot <= 0 || lastDot === trimmed.length - 1) {
58855
+ return { base: trimmed, ext: "" };
58856
+ }
58857
+ return {
58858
+ base: trimmed.slice(0, lastDot),
58859
+ ext: trimmed.slice(lastDot + 1)
58860
+ };
58861
+ };
58862
+ const sanitizeImageFileName = (inputName) => {
58863
+ const { base: base2, ext } = splitFileName(inputName || "");
58864
+ const sanitizedBase = sanitizeSegment(base2, { allowDots: true }) || FALLBACK_NAME;
58865
+ const sanitizedExt = sanitizeSegment(ext, { allowDots: false }).toLowerCase();
58866
+ if (!sanitizedExt) return sanitizedBase;
58867
+ return `${sanitizedBase}.${sanitizedExt}`;
58868
+ };
58869
+ const ensureUniqueFileName = (preferredName, existingNames = /* @__PURE__ */ new Set()) => {
58870
+ const sanitized = sanitizeImageFileName(preferredName);
58871
+ if (!existingNames || typeof existingNames.has !== "function") {
58872
+ return sanitized;
58873
+ }
58874
+ const existingSet = /* @__PURE__ */ new Set();
58875
+ existingNames.forEach((name) => existingSet.add(sanitizeImageFileName(name)));
58876
+ if (!existingSet.has(sanitized)) {
58877
+ return sanitized;
58878
+ }
58879
+ const { base: base2, ext } = splitFileName(sanitized);
58880
+ let counter = 1;
58881
+ let candidate = sanitized;
58882
+ const suffix2 = () => `${base2}-${counter}${ext ? `.${ext}` : ""}`;
58883
+ while (existingSet.has(candidate)) {
58884
+ candidate = suffix2();
58885
+ counter += 1;
58886
+ }
58887
+ return candidate;
58888
+ };
58889
+ const buildMediaPath = (fileName) => `word/media/${fileName}`;
58839
58890
  const fileTooLarge = (file) => {
58840
58891
  let fileSizeMb = Number((file.size / (1024 * 1024)).toFixed(4));
58841
58892
  if (fileSizeMb > 5) {
@@ -58869,16 +58920,37 @@ function replaceSelectionWithImagePlaceholder({ editorOptions, view, id }) {
58869
58920
  tr = addImagePlaceholder(view.state, tr, id, selection.from);
58870
58921
  view.dispatch(tr);
58871
58922
  }
58923
+ const generateUniqueDocPrId = (editor) => {
58924
+ const existingIds = /* @__PURE__ */ new Set();
58925
+ editor?.state?.doc?.descendants((node) => {
58926
+ if (node.type.name === "image" && node.attrs.id !== void 0 && node.attrs.id !== null) {
58927
+ existingIds.add(String(node.attrs.id));
58928
+ }
58929
+ });
58930
+ let candidate;
58931
+ do {
58932
+ const hex2 = generateDocxRandomId();
58933
+ candidate = String(parseInt(hex2, 16));
58934
+ } while (!candidate || existingIds.has(candidate));
58935
+ return candidate;
58936
+ };
58872
58937
  async function uploadAndInsertImage({ editor, view, file, size: size2, id }) {
58873
58938
  const imageUploadHandler = typeof editor.options.handleImageUpload === "function" ? editor.options.handleImageUpload : handleImageUpload;
58939
+ const placeholderId = id;
58874
58940
  try {
58875
- let url = await imageUploadHandler(file);
58876
- let fileName = file.name.replace(" ", "_");
58877
- let placeholderPos = findPlaceholder(view.state, id);
58941
+ const existingFileNames = new Set(Object.keys(editor.storage.image.media ?? {}).map((key2) => key2.split("/").pop()));
58942
+ const uniqueFileName = ensureUniqueFileName(file.name, existingFileNames);
58943
+ const normalizedFile = uniqueFileName === file.name ? file : new File([file], uniqueFileName, {
58944
+ type: file.type,
58945
+ lastModified: file.lastModified ?? Date.now()
58946
+ });
58947
+ let url = await imageUploadHandler(normalizedFile);
58948
+ let placeholderPos = findPlaceholder(view.state, placeholderId);
58878
58949
  if (placeholderPos == null) {
58879
58950
  return;
58880
58951
  }
58881
- let mediaPath = `word/media/${fileName}`;
58952
+ const mediaPath = buildMediaPath(uniqueFileName);
58953
+ const docPrId = generateUniqueDocPrId(editor);
58882
58954
  let rId = null;
58883
58955
  if (editor.options.mode === "docx") {
58884
58956
  const [, path] = mediaPath.split("word/");
@@ -58888,7 +58960,7 @@ async function uploadAndInsertImage({ editor, view, file, size: size2, id }) {
58888
58960
  let imageNode = view.state.schema.nodes.image.create({
58889
58961
  src: mediaPath,
58890
58962
  size: size2,
58891
- id,
58963
+ id: docPrId,
58892
58964
  rId
58893
58965
  });
58894
58966
  editor.storage.image.media = Object.assign(editor.storage.image.media, { [mediaPath]: url });
@@ -58897,10 +58969,10 @@ async function uploadAndInsertImage({ editor, view, file, size: size2, id }) {
58897
58969
  }
58898
58970
  let tr = view.state.tr;
58899
58971
  tr.replaceWith(placeholderPos, placeholderPos, imageNode);
58900
- tr = removeImagePlaceholder(view.state, tr, id);
58972
+ tr = removeImagePlaceholder(view.state, tr, placeholderId);
58901
58973
  view.dispatch(tr);
58902
58974
  } catch {
58903
- const tr = removeImagePlaceholder(view.state, view.state.tr, id);
58975
+ const tr = removeImagePlaceholder(view.state, view.state.tr, placeholderId);
58904
58976
  view.dispatch(tr);
58905
58977
  }
58906
58978
  }
@@ -58819,6 +58819,57 @@ function multiStepResize(canvas, width, height) {
58819
58819
  }
58820
58820
  resample_high_quality(canvas, width, height);
58821
58821
  }
58822
+ const FALLBACK_NAME = "image";
58823
+ const stripDiacritics = (value) => value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
58824
+ const sanitizeSegment = (segment, { allowDots = false } = {}) => {
58825
+ if (!segment) return "";
58826
+ const normalized = stripDiacritics(segment).replace(/[\s\u2000-\u206f]+/g, "_").replace(/[\\/]+/g, "_");
58827
+ const allowedPattern = allowDots ? /[^0-9A-Za-z._-]+/g : /[^0-9A-Za-z_-]+/g;
58828
+ let sanitized = normalized.replace(allowedPattern, "_");
58829
+ sanitized = sanitized.replace(/_+/g, "_");
58830
+ sanitized = sanitized.replace(/^[_.-]+/, "");
58831
+ sanitized = sanitized.replace(/[_-]+$/, "");
58832
+ return sanitized;
58833
+ };
58834
+ const splitFileName = (name) => {
58835
+ const trimmed = name?.trim?.() ?? "";
58836
+ const lastDot = trimmed.lastIndexOf(".");
58837
+ if (lastDot <= 0 || lastDot === trimmed.length - 1) {
58838
+ return { base: trimmed, ext: "" };
58839
+ }
58840
+ return {
58841
+ base: trimmed.slice(0, lastDot),
58842
+ ext: trimmed.slice(lastDot + 1)
58843
+ };
58844
+ };
58845
+ const sanitizeImageFileName = (inputName) => {
58846
+ const { base: base2, ext } = splitFileName(inputName || "");
58847
+ const sanitizedBase = sanitizeSegment(base2, { allowDots: true }) || FALLBACK_NAME;
58848
+ const sanitizedExt = sanitizeSegment(ext, { allowDots: false }).toLowerCase();
58849
+ if (!sanitizedExt) return sanitizedBase;
58850
+ return `${sanitizedBase}.${sanitizedExt}`;
58851
+ };
58852
+ const ensureUniqueFileName = (preferredName, existingNames = /* @__PURE__ */ new Set()) => {
58853
+ const sanitized = sanitizeImageFileName(preferredName);
58854
+ if (!existingNames || typeof existingNames.has !== "function") {
58855
+ return sanitized;
58856
+ }
58857
+ const existingSet = /* @__PURE__ */ new Set();
58858
+ existingNames.forEach((name) => existingSet.add(sanitizeImageFileName(name)));
58859
+ if (!existingSet.has(sanitized)) {
58860
+ return sanitized;
58861
+ }
58862
+ const { base: base2, ext } = splitFileName(sanitized);
58863
+ let counter = 1;
58864
+ let candidate = sanitized;
58865
+ const suffix2 = () => `${base2}-${counter}${ext ? `.${ext}` : ""}`;
58866
+ while (existingSet.has(candidate)) {
58867
+ candidate = suffix2();
58868
+ counter += 1;
58869
+ }
58870
+ return candidate;
58871
+ };
58872
+ const buildMediaPath = (fileName) => `word/media/${fileName}`;
58822
58873
  const fileTooLarge = (file) => {
58823
58874
  let fileSizeMb = Number((file.size / (1024 * 1024)).toFixed(4));
58824
58875
  if (fileSizeMb > 5) {
@@ -58852,16 +58903,37 @@ function replaceSelectionWithImagePlaceholder({ editorOptions, view, id }) {
58852
58903
  tr = addImagePlaceholder(view.state, tr, id, selection.from);
58853
58904
  view.dispatch(tr);
58854
58905
  }
58906
+ const generateUniqueDocPrId = (editor) => {
58907
+ const existingIds = /* @__PURE__ */ new Set();
58908
+ editor?.state?.doc?.descendants((node) => {
58909
+ if (node.type.name === "image" && node.attrs.id !== void 0 && node.attrs.id !== null) {
58910
+ existingIds.add(String(node.attrs.id));
58911
+ }
58912
+ });
58913
+ let candidate;
58914
+ do {
58915
+ const hex2 = generateDocxRandomId();
58916
+ candidate = String(parseInt(hex2, 16));
58917
+ } while (!candidate || existingIds.has(candidate));
58918
+ return candidate;
58919
+ };
58855
58920
  async function uploadAndInsertImage({ editor, view, file, size: size2, id }) {
58856
58921
  const imageUploadHandler = typeof editor.options.handleImageUpload === "function" ? editor.options.handleImageUpload : handleImageUpload;
58922
+ const placeholderId = id;
58857
58923
  try {
58858
- let url = await imageUploadHandler(file);
58859
- let fileName = file.name.replace(" ", "_");
58860
- let placeholderPos = findPlaceholder(view.state, id);
58924
+ const existingFileNames = new Set(Object.keys(editor.storage.image.media ?? {}).map((key2) => key2.split("/").pop()));
58925
+ const uniqueFileName = ensureUniqueFileName(file.name, existingFileNames);
58926
+ const normalizedFile = uniqueFileName === file.name ? file : new File([file], uniqueFileName, {
58927
+ type: file.type,
58928
+ lastModified: file.lastModified ?? Date.now()
58929
+ });
58930
+ let url = await imageUploadHandler(normalizedFile);
58931
+ let placeholderPos = findPlaceholder(view.state, placeholderId);
58861
58932
  if (placeholderPos == null) {
58862
58933
  return;
58863
58934
  }
58864
- let mediaPath = `word/media/${fileName}`;
58935
+ const mediaPath = buildMediaPath(uniqueFileName);
58936
+ const docPrId = generateUniqueDocPrId(editor);
58865
58937
  let rId = null;
58866
58938
  if (editor.options.mode === "docx") {
58867
58939
  const [, path] = mediaPath.split("word/");
@@ -58871,7 +58943,7 @@ async function uploadAndInsertImage({ editor, view, file, size: size2, id }) {
58871
58943
  let imageNode = view.state.schema.nodes.image.create({
58872
58944
  src: mediaPath,
58873
58945
  size: size2,
58874
- id,
58946
+ id: docPrId,
58875
58947
  rId
58876
58948
  });
58877
58949
  editor.storage.image.media = Object.assign(editor.storage.image.media, { [mediaPath]: url });
@@ -58880,10 +58952,10 @@ async function uploadAndInsertImage({ editor, view, file, size: size2, id }) {
58880
58952
  }
58881
58953
  let tr = view.state.tr;
58882
58954
  tr.replaceWith(placeholderPos, placeholderPos, imageNode);
58883
- tr = removeImagePlaceholder(view.state, tr, id);
58955
+ tr = removeImagePlaceholder(view.state, tr, placeholderId);
58884
58956
  view.dispatch(tr);
58885
58957
  } catch {
58886
- const tr = removeImagePlaceholder(view.state, view.state.tr, id);
58958
+ const tr = removeImagePlaceholder(view.state, view.state.tr, placeholderId);
58887
58959
  view.dispatch(tr);
58888
58960
  }
58889
58961
  }
@@ -1,6 +1,6 @@
1
1
  import { ref, onMounted, onUnmounted, computed, createElementBlock, openBlock, withModifiers, createElementVNode, withDirectives, unref, vModelText, createCommentVNode, nextTick } from "vue";
2
2
  import { T as TextSelection } from "./chunks/converter-kutpjDQU.js";
3
- import { _ as _export_sfc } from "./chunks/editor-ps-v4FlA.js";
3
+ import { _ as _export_sfc } from "./chunks/editor-CQMKOaY4.js";
4
4
  const DEFAULT_API_ENDPOINT = "https://sd-dev-express-gateway-i6xtm.ondigitalocean.app/insights";
5
5
  const SYSTEM_PROMPT = "You are an expert copywriter and you are immersed in a document editor. You are to provide document related text responses based on the user prompts. Only write what is asked for. Do not provide explanations. Try to keep placeholders as short as possible. Do not output your prompt. Your instructions are: ";
6
6
  async function baseInsightsFetch(payload, options = {}) {
@@ -24477,6 +24477,57 @@ function multiStepResize(canvas, width, height) {
24477
24477
  }
24478
24478
  resample_high_quality(canvas, width, height);
24479
24479
  }
24480
+ const FALLBACK_NAME = "image";
24481
+ const stripDiacritics = (value) => value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
24482
+ const sanitizeSegment = (segment, { allowDots = false } = {}) => {
24483
+ if (!segment) return "";
24484
+ const normalized = stripDiacritics(segment).replace(/[\s\u2000-\u206f]+/g, "_").replace(/[\\/]+/g, "_");
24485
+ const allowedPattern = allowDots ? /[^0-9A-Za-z._-]+/g : /[^0-9A-Za-z_-]+/g;
24486
+ let sanitized = normalized.replace(allowedPattern, "_");
24487
+ sanitized = sanitized.replace(/_+/g, "_");
24488
+ sanitized = sanitized.replace(/^[_.-]+/, "");
24489
+ sanitized = sanitized.replace(/[_-]+$/, "");
24490
+ return sanitized;
24491
+ };
24492
+ const splitFileName = (name) => {
24493
+ const trimmed = name?.trim?.() ?? "";
24494
+ const lastDot = trimmed.lastIndexOf(".");
24495
+ if (lastDot <= 0 || lastDot === trimmed.length - 1) {
24496
+ return { base: trimmed, ext: "" };
24497
+ }
24498
+ return {
24499
+ base: trimmed.slice(0, lastDot),
24500
+ ext: trimmed.slice(lastDot + 1)
24501
+ };
24502
+ };
24503
+ const sanitizeImageFileName = (inputName) => {
24504
+ const { base: base2, ext } = splitFileName(inputName || "");
24505
+ const sanitizedBase = sanitizeSegment(base2, { allowDots: true }) || FALLBACK_NAME;
24506
+ const sanitizedExt = sanitizeSegment(ext, { allowDots: false }).toLowerCase();
24507
+ if (!sanitizedExt) return sanitizedBase;
24508
+ return `${sanitizedBase}.${sanitizedExt}`;
24509
+ };
24510
+ const ensureUniqueFileName = (preferredName, existingNames = /* @__PURE__ */ new Set()) => {
24511
+ const sanitized = sanitizeImageFileName(preferredName);
24512
+ if (!existingNames || typeof existingNames.has !== "function") {
24513
+ return sanitized;
24514
+ }
24515
+ const existingSet = /* @__PURE__ */ new Set();
24516
+ existingNames.forEach((name) => existingSet.add(sanitizeImageFileName(name)));
24517
+ if (!existingSet.has(sanitized)) {
24518
+ return sanitized;
24519
+ }
24520
+ const { base: base2, ext } = splitFileName(sanitized);
24521
+ let counter = 1;
24522
+ let candidate = sanitized;
24523
+ const suffix = () => `${base2}-${counter}${ext ? `.${ext}` : ""}`;
24524
+ while (existingSet.has(candidate)) {
24525
+ candidate = suffix();
24526
+ counter += 1;
24527
+ }
24528
+ return candidate;
24529
+ };
24530
+ const buildMediaPath = (fileName) => `word/media/${fileName}`;
24480
24531
  const fileTooLarge = (file) => {
24481
24532
  let fileSizeMb = Number((file.size / (1024 * 1024)).toFixed(4));
24482
24533
  if (fileSizeMb > 5) {
@@ -24510,16 +24561,37 @@ function replaceSelectionWithImagePlaceholder({ editorOptions, view, id }) {
24510
24561
  tr = addImagePlaceholder(view.state, tr, id, selection.from);
24511
24562
  view.dispatch(tr);
24512
24563
  }
24564
+ const generateUniqueDocPrId = (editor) => {
24565
+ const existingIds = /* @__PURE__ */ new Set();
24566
+ editor?.state?.doc?.descendants((node) => {
24567
+ if (node.type.name === "image" && node.attrs.id !== void 0 && node.attrs.id !== null) {
24568
+ existingIds.add(String(node.attrs.id));
24569
+ }
24570
+ });
24571
+ let candidate;
24572
+ do {
24573
+ const hex = generateDocxRandomId();
24574
+ candidate = String(parseInt(hex, 16));
24575
+ } while (!candidate || existingIds.has(candidate));
24576
+ return candidate;
24577
+ };
24513
24578
  async function uploadAndInsertImage({ editor, view, file, size, id }) {
24514
24579
  const imageUploadHandler = typeof editor.options.handleImageUpload === "function" ? editor.options.handleImageUpload : handleImageUpload;
24580
+ const placeholderId = id;
24515
24581
  try {
24516
- let url = await imageUploadHandler(file);
24517
- let fileName = file.name.replace(" ", "_");
24518
- let placeholderPos = findPlaceholder(view.state, id);
24582
+ const existingFileNames = new Set(Object.keys(editor.storage.image.media ?? {}).map((key2) => key2.split("/").pop()));
24583
+ const uniqueFileName = ensureUniqueFileName(file.name, existingFileNames);
24584
+ const normalizedFile = uniqueFileName === file.name ? file : new File([file], uniqueFileName, {
24585
+ type: file.type,
24586
+ lastModified: file.lastModified ?? Date.now()
24587
+ });
24588
+ let url = await imageUploadHandler(normalizedFile);
24589
+ let placeholderPos = findPlaceholder(view.state, placeholderId);
24519
24590
  if (placeholderPos == null) {
24520
24591
  return;
24521
24592
  }
24522
- let mediaPath = `word/media/${fileName}`;
24593
+ const mediaPath = buildMediaPath(uniqueFileName);
24594
+ const docPrId = generateUniqueDocPrId(editor);
24523
24595
  let rId = null;
24524
24596
  if (editor.options.mode === "docx") {
24525
24597
  const [, path] = mediaPath.split("word/");
@@ -24529,7 +24601,7 @@ async function uploadAndInsertImage({ editor, view, file, size, id }) {
24529
24601
  let imageNode = view.state.schema.nodes.image.create({
24530
24602
  src: mediaPath,
24531
24603
  size,
24532
- id,
24604
+ id: docPrId,
24533
24605
  rId
24534
24606
  });
24535
24607
  editor.storage.image.media = Object.assign(editor.storage.image.media, { [mediaPath]: url });
@@ -24538,10 +24610,10 @@ async function uploadAndInsertImage({ editor, view, file, size, id }) {
24538
24610
  }
24539
24611
  let tr = view.state.tr;
24540
24612
  tr.replaceWith(placeholderPos, placeholderPos, imageNode);
24541
- tr = removeImagePlaceholder(view.state, tr, id);
24613
+ tr = removeImagePlaceholder(view.state, tr, placeholderId);
24542
24614
  view.dispatch(tr);
24543
24615
  } catch {
24544
- const tr = removeImagePlaceholder(view.state, view.state.tr, id);
24616
+ const tr = removeImagePlaceholder(view.state, view.state.tr, placeholderId);
24545
24617
  view.dispatch(tr);
24546
24618
  }
24547
24619
  }
@@ -1,6 +1,6 @@
1
1
  import { computed, createElementBlock, openBlock, createElementVNode, createCommentVNode, normalizeClass, normalizeStyle, ref, withKeys, unref, withModifiers, createBlock, toDisplayString, withDirectives, vModelText, nextTick, getCurrentInstance, createVNode, readonly, watch, onMounted, onBeforeUnmount, reactive, onBeforeMount, inject, onActivated, onDeactivated, createTextVNode, Fragment, Comment, defineComponent, provide, h, Teleport, toRef, renderSlot, isVNode, shallowRef, watchEffect, mergeProps, Transition, vShow, cloneVNode, Text, renderList, withCtx } from "vue";
2
2
  import { p as process$1 } from "./converter-kutpjDQU.js";
3
- import { _ as _export_sfc, u as useHighContrastMode, g as global$1 } from "./editor-ps-v4FlA.js";
3
+ import { _ as _export_sfc, u as useHighContrastMode, g as global$1 } from "./editor-CQMKOaY4.js";
4
4
  const sanitizeNumber = (value, defaultNumber) => {
5
5
  let sanitized = value.replace(/[^0-9.]/g, "");
6
6
  sanitized = parseFloat(sanitized);
@@ -1,4 +1,4 @@
1
- import { E } from "./chunks/editor-ps-v4FlA.js";
1
+ import { E } from "./chunks/editor-CQMKOaY4.js";
2
2
  import "./chunks/converter-kutpjDQU.js";
3
3
  import "./chunks/docx-zipper-BjcI24VU.js";
4
4
  export {
@@ -0,0 +1,3 @@
1
+ export function sanitizeImageFileName(inputName: any): any;
2
+ export function ensureUniqueFileName(preferredName: any, existingNames?: Set<any>): any;
3
+ export function buildMediaPath(fileName: any): string;
@@ -4,3 +4,4 @@ export * from "./handleImageUpload.js";
4
4
  export * from "./imageRegistrationPlugin.js";
5
5
  export * from "./processUploadedImage.js";
6
6
  export * from "./imagePositionPlugin.js";
7
+ export * from "./fileNameUtils.js";
@@ -11,10 +11,10 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
11
11
  var _SuperToolbar_instances, initToolbarGroups_fn, _interceptedCommands, makeToolbarItems_fn, initDefaultFonts_fn, updateHighlightColors_fn, deactivateAll_fn, updateToolbarHistory_fn, runCommandWithArgumentOnly_fn;
12
12
  import { aw as getDefaultExportFromCjs, V as v4, T as TextSelection$1, v as getMarkRange, az as vClickOutside, H as findParentNode, aA as getActiveFormatting, aq as isInTable, aB as readFromClipboard, aC as handleClipboardPaste, aD as getFileObject, a as Plugin } from "./chunks/converter-kutpjDQU.js";
13
13
  import { aE, a5, i, a2, aF } from "./chunks/converter-kutpjDQU.js";
14
- import { _ as _export_sfc, u as useHighContrastMode, a as getQuickFormatList, b as generateLinkedStyleString, c as getFileOpener, d as checkAndProcessImage, r as replaceSelectionWithImagePlaceholder, e as uploadAndInsertImage, y as yUndoPluginKey, f as undoDepth, h as redoDepth, S as SlashMenuPluginKey, E as Editor, i as getStarterExtensions, P as Placeholder, j as getRichTextExtensions, M as Mark, k as Extension, A as Attribute, N as Node } from "./chunks/editor-ps-v4FlA.js";
15
- import { n, C, o, T, l, p, m } from "./chunks/editor-ps-v4FlA.js";
14
+ import { _ as _export_sfc, u as useHighContrastMode, a as getQuickFormatList, b as generateLinkedStyleString, c as getFileOpener, d as checkAndProcessImage, r as replaceSelectionWithImagePlaceholder, e as uploadAndInsertImage, y as yUndoPluginKey, f as undoDepth, h as redoDepth, S as SlashMenuPluginKey, E as Editor, i as getStarterExtensions, P as Placeholder, j as getRichTextExtensions, M as Mark, k as Extension, A as Attribute, N as Node } from "./chunks/editor-CQMKOaY4.js";
15
+ import { n, C, o, T, l, p, m } from "./chunks/editor-CQMKOaY4.js";
16
16
  import { ref, onMounted, createElementBlock, openBlock, normalizeClass, unref, Fragment, renderList, createElementVNode, withModifiers, toDisplayString, createCommentVNode, normalizeStyle, computed, watch, withDirectives, withKeys, vModelText, createTextVNode, createVNode, h, createApp, markRaw, nextTick, onBeforeUnmount, reactive, onUnmounted, renderSlot, shallowRef, createBlock, withCtx, resolveDynamicComponent, normalizeProps, guardReactiveProps } from "vue";
17
- import { t as toolbarIcons, s as sanitizeNumber, T as Toolbar, m as magicWandIcon, p as plusIconSvg, a as trashIconSvg, l as linkIconSvg, b as tableIconSvg, c as scissorsIconSvg, d as copyIconSvg, e as pasteIconSvg, f as borderNoneIconSvg, g as arrowsToDotIconSvg, h as arrowsLeftRightIconSvg, w as wrenchIconSvg, u as useMessage, N as NSkeleton } from "./chunks/toolbar-Ch271j8X.js";
17
+ import { t as toolbarIcons, s as sanitizeNumber, T as Toolbar, m as magicWandIcon, p as plusIconSvg, a as trashIconSvg, l as linkIconSvg, b as tableIconSvg, c as scissorsIconSvg, d as copyIconSvg, e as pasteIconSvg, f as borderNoneIconSvg, g as arrowsToDotIconSvg, h as arrowsLeftRightIconSvg, w as wrenchIconSvg, u as useMessage, N as NSkeleton } from "./chunks/toolbar-Cp6gL4i5.js";
18
18
  import AIWriter from "./ai-writer.es.js";
19
19
  import { D } from "./chunks/docx-zipper-BjcI24VU.js";
20
20
  import { createZip } from "./file-zipper.es.js";
@@ -1,6 +1,6 @@
1
1
  import "vue";
2
- import { T } from "./chunks/toolbar-Ch271j8X.js";
3
- import "./chunks/editor-ps-v4FlA.js";
2
+ import { T } from "./chunks/toolbar-Cp6gL4i5.js";
3
+ import "./chunks/editor-CQMKOaY4.js";
4
4
  export {
5
5
  T as default
6
6
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const superEditor_es = require("./chunks/super-editor.es-BIW7iKAk.cjs");
3
+ const superEditor_es = require("./chunks/super-editor.es-5O71lyiI.cjs");
4
4
  require("./chunks/vue-DWle4Cai.cjs");
5
5
  exports.AIWriter = superEditor_es.AIWriter;
6
6
  exports.AnnotatorHelpers = superEditor_es.AnnotatorHelpers;
@@ -1,4 +1,4 @@
1
- import { A, a, _, C, D, E, b, S, c, d, e, f, g, T, h, i, j, k, l, m, n, o, p, r, q } from "./chunks/super-editor.es-DN4v75aq.es.js";
1
+ import { A, a, _, C, D, E, b, S, c, d, e, f, g, T, h, i, j, k, l, m, n, o, p, r, q } from "./chunks/super-editor.es-BTalhwn-.es.js";
2
2
  import "./chunks/vue-CXxsqYcP.es.js";
3
3
  export {
4
4
  A as AIWriter,
package/dist/superdoc.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const superEditor_es = require("./chunks/super-editor.es-BIW7iKAk.cjs");
4
- const superdoc = require("./chunks/index-CuHtS7O9.cjs");
3
+ const superEditor_es = require("./chunks/super-editor.es-5O71lyiI.cjs");
4
+ const superdoc = require("./chunks/index-CMBPzhIR.cjs");
5
5
  require("./chunks/vue-DWle4Cai.cjs");
6
6
  require("./chunks/jszip-b7l8QkfH.cjs");
7
7
  const blankDocx = require("./chunks/blank-docx-CPqX9RF5.cjs");
@@ -1,5 +1,5 @@
1
- import { a, E, b, S, d, i, j, n, r, p, q } from "./chunks/super-editor.es-DN4v75aq.es.js";
2
- import { D, H, P, S as S2, m, l } from "./chunks/index-Cl1u_lKk.es.js";
1
+ import { a, E, b, S, d, i, j, n, r, p, q } from "./chunks/super-editor.es-BTalhwn-.es.js";
2
+ import { D, H, P, S as S2, m, l } from "./chunks/index-CqLcgYHG.es.js";
3
3
  import "./chunks/vue-CXxsqYcP.es.js";
4
4
  import "./chunks/jszip-B8KIZSNe.es.js";
5
5
  import { B } from "./chunks/blank-docx-iwdyG9RH.es.js";
@@ -66534,6 +66534,57 @@ Please report this to https://github.com/markedjs/marked.`, e) {
66534
66534
  }
66535
66535
  resample_high_quality(canvas, width, height);
66536
66536
  }
66537
+ const FALLBACK_NAME = "image";
66538
+ const stripDiacritics = (value) => value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
66539
+ const sanitizeSegment = (segment, { allowDots = false } = {}) => {
66540
+ if (!segment) return "";
66541
+ const normalized = stripDiacritics(segment).replace(/[\s\u2000-\u206f]+/g, "_").replace(/[\\/]+/g, "_");
66542
+ const allowedPattern = allowDots ? /[^0-9A-Za-z._-]+/g : /[^0-9A-Za-z_-]+/g;
66543
+ let sanitized = normalized.replace(allowedPattern, "_");
66544
+ sanitized = sanitized.replace(/_+/g, "_");
66545
+ sanitized = sanitized.replace(/^[_.-]+/, "");
66546
+ sanitized = sanitized.replace(/[_-]+$/, "");
66547
+ return sanitized;
66548
+ };
66549
+ const splitFileName = (name) => {
66550
+ const trimmed = name?.trim?.() ?? "";
66551
+ const lastDot = trimmed.lastIndexOf(".");
66552
+ if (lastDot <= 0 || lastDot === trimmed.length - 1) {
66553
+ return { base: trimmed, ext: "" };
66554
+ }
66555
+ return {
66556
+ base: trimmed.slice(0, lastDot),
66557
+ ext: trimmed.slice(lastDot + 1)
66558
+ };
66559
+ };
66560
+ const sanitizeImageFileName = (inputName) => {
66561
+ const { base: base2, ext } = splitFileName(inputName || "");
66562
+ const sanitizedBase = sanitizeSegment(base2, { allowDots: true }) || FALLBACK_NAME;
66563
+ const sanitizedExt = sanitizeSegment(ext, { allowDots: false }).toLowerCase();
66564
+ if (!sanitizedExt) return sanitizedBase;
66565
+ return `${sanitizedBase}.${sanitizedExt}`;
66566
+ };
66567
+ const ensureUniqueFileName = (preferredName, existingNames = /* @__PURE__ */ new Set()) => {
66568
+ const sanitized = sanitizeImageFileName(preferredName);
66569
+ if (!existingNames || typeof existingNames.has !== "function") {
66570
+ return sanitized;
66571
+ }
66572
+ const existingSet = /* @__PURE__ */ new Set();
66573
+ existingNames.forEach((name) => existingSet.add(sanitizeImageFileName(name)));
66574
+ if (!existingSet.has(sanitized)) {
66575
+ return sanitized;
66576
+ }
66577
+ const { base: base2, ext } = splitFileName(sanitized);
66578
+ let counter = 1;
66579
+ let candidate = sanitized;
66580
+ const suffix2 = () => `${base2}-${counter}${ext ? `.${ext}` : ""}`;
66581
+ while (existingSet.has(candidate)) {
66582
+ candidate = suffix2();
66583
+ counter += 1;
66584
+ }
66585
+ return candidate;
66586
+ };
66587
+ const buildMediaPath = (fileName) => `word/media/${fileName}`;
66537
66588
  const fileTooLarge = (file) => {
66538
66589
  let fileSizeMb = Number((file.size / (1024 * 1024)).toFixed(4));
66539
66590
  if (fileSizeMb > 5) {
@@ -66567,16 +66618,37 @@ Please report this to https://github.com/markedjs/marked.`, e) {
66567
66618
  tr = addImagePlaceholder(view.state, tr, id, selection.from);
66568
66619
  view.dispatch(tr);
66569
66620
  }
66621
+ const generateUniqueDocPrId = (editor) => {
66622
+ const existingIds = /* @__PURE__ */ new Set();
66623
+ editor?.state?.doc?.descendants((node) => {
66624
+ if (node.type.name === "image" && node.attrs.id !== void 0 && node.attrs.id !== null) {
66625
+ existingIds.add(String(node.attrs.id));
66626
+ }
66627
+ });
66628
+ let candidate;
66629
+ do {
66630
+ const hex2 = generateDocxRandomId();
66631
+ candidate = String(parseInt(hex2, 16));
66632
+ } while (!candidate || existingIds.has(candidate));
66633
+ return candidate;
66634
+ };
66570
66635
  async function uploadAndInsertImage({ editor, view, file, size: size2, id }) {
66571
66636
  const imageUploadHandler = typeof editor.options.handleImageUpload === "function" ? editor.options.handleImageUpload : handleImageUpload;
66637
+ const placeholderId = id;
66572
66638
  try {
66573
- let url = await imageUploadHandler(file);
66574
- let fileName = file.name.replace(" ", "_");
66575
- let placeholderPos = findPlaceholder(view.state, id);
66639
+ const existingFileNames = new Set(Object.keys(editor.storage.image.media ?? {}).map((key2) => key2.split("/").pop()));
66640
+ const uniqueFileName = ensureUniqueFileName(file.name, existingFileNames);
66641
+ const normalizedFile = uniqueFileName === file.name ? file : new File([file], uniqueFileName, {
66642
+ type: file.type,
66643
+ lastModified: file.lastModified ?? Date.now()
66644
+ });
66645
+ let url = await imageUploadHandler(normalizedFile);
66646
+ let placeholderPos = findPlaceholder(view.state, placeholderId);
66576
66647
  if (placeholderPos == null) {
66577
66648
  return;
66578
66649
  }
66579
- let mediaPath = `word/media/${fileName}`;
66650
+ const mediaPath = buildMediaPath(uniqueFileName);
66651
+ const docPrId = generateUniqueDocPrId(editor);
66580
66652
  let rId = null;
66581
66653
  if (editor.options.mode === "docx") {
66582
66654
  const [, path] = mediaPath.split("word/");
@@ -66586,7 +66658,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
66586
66658
  let imageNode = view.state.schema.nodes.image.create({
66587
66659
  src: mediaPath,
66588
66660
  size: size2,
66589
- id,
66661
+ id: docPrId,
66590
66662
  rId
66591
66663
  });
66592
66664
  editor.storage.image.media = Object.assign(editor.storage.image.media, { [mediaPath]: url });
@@ -66595,10 +66667,10 @@ Please report this to https://github.com/markedjs/marked.`, e) {
66595
66667
  }
66596
66668
  let tr = view.state.tr;
66597
66669
  tr.replaceWith(placeholderPos, placeholderPos, imageNode);
66598
- tr = removeImagePlaceholder(view.state, tr, id);
66670
+ tr = removeImagePlaceholder(view.state, tr, placeholderId);
66599
66671
  view.dispatch(tr);
66600
66672
  } catch {
66601
- const tr = removeImagePlaceholder(view.state, view.state.tr, id);
66673
+ const tr = removeImagePlaceholder(view.state, view.state.tr, placeholderId);
66602
66674
  view.dispatch(tr);
66603
66675
  }
66604
66676
  }