superdoc 2.0.0-next.49 → 2.0.0-next.50

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 (29) hide show
  1. package/AGENTS.md +31 -10
  2. package/dist/chunks/{create-super-doc-ui-BmtUtDDR.es.js → create-super-doc-ui-D4UDePxr.es.js} +126 -28
  3. package/dist/chunks/{create-super-doc-ui-BNo2DvD1.cjs → create-super-doc-ui-DlSN0ENQ.cjs} +126 -28
  4. package/dist/collaboration-upgrade-engine.cjs +1 -1
  5. package/dist/collaboration-upgrade-engine.es.js +1 -1
  6. package/dist/document-api/src/contract/operation-definitions.d.ts +1 -1
  7. package/dist/public/ui-react.cjs +1 -1
  8. package/dist/public/ui-react.es.js +1 -1
  9. package/dist/public/ui.cjs +1 -1
  10. package/dist/public/ui.es.js +1 -1
  11. package/dist/shared/font-system/src/font-offerings.d.ts +1 -1
  12. package/dist/style.css +28 -28
  13. package/dist/style.layered.css +28 -28
  14. package/dist/superdoc/src/core/SuperDoc.d.ts +43 -1
  15. package/dist/superdoc/src/core/config/merge-defined.d.ts +35 -0
  16. package/dist/superdoc/src/core/config/normalize-interaction-config.d.ts +15 -0
  17. package/dist/superdoc/src/core/config/normalize-surfaces-config.d.ts +23 -0
  18. package/dist/superdoc/src/core/config/normalize-ui-config.d.ts +62 -0
  19. package/dist/superdoc/src/core/types/index.d.ts +209 -2
  20. package/dist/superdoc/src/public/index.d.cts +6 -0
  21. package/dist/superdoc/src/public/index.d.ts +3 -0
  22. package/dist/superdoc/src/public/ui.d.cts +2 -0
  23. package/dist/superdoc/src/public/ui.d.ts +1 -1
  24. package/dist/superdoc.cjs +211 -37
  25. package/dist/superdoc.es.js +211 -37
  26. package/dist-cdn/style.layered.css +1 -1
  27. package/dist-cdn/superdoc.min.css +1 -1
  28. package/dist-cdn/superdoc.min.js +37 -37
  29. package/package.json +2 -2
@@ -5,7 +5,7 @@ import { t as v4_default } from "./chunks/uuid-B2Sqk-3p.es.js";
5
5
  import { a as init_dist, i as global, n as init_dist$1, r as process$1, t as require_jszip_min } from "./chunks/jszip-DzmwAHr3.es.js";
6
6
  import { t as __plugin_vue_export_helper_default } from "./chunks/_plugin-vue_export-helper-CInC0bKI.es.js";
7
7
  import { n as PDF_TO_CSS_UNITS } from "./chunks/constants-CY3R3_kF.es.js";
8
- import { a as createV2ReviewMutationReconciler, i as isV2EditableTextMutationEvent, o as getV2TrackedChangeMutationImpact, s as composeAuthorColorResolver, t as createSuperDocUI } from "./chunks/create-super-doc-ui-BmtUtDDR.es.js";
8
+ import { a as createV2ReviewMutationReconciler, i as isV2EditableTextMutationEvent, o as getV2TrackedChangeMutationImpact, s as composeAuthorColorResolver, t as createSuperDocUI } from "./chunks/create-super-doc-ui-D4UDePxr.es.js";
9
9
  import * as Vue from "vue";
10
10
  import { Fragment, Teleport, Transition, computed, createApp, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineAsyncComponent, defineComponent, getCurrentInstance, h, inject, markRaw, mergeProps, nextTick, normalizeClass, normalizeProps, normalizeStyle, onActivated, onBeforeUnmount, onDeactivated, onMounted, openBlock, reactive, ref, renderList, renderSlot, resolveDirective, resolveDynamicComponent, shallowRef, toDisplayString, toRaw, toRef, unref, useAttrs, vModelText, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
11
11
  import "y-websocket";
@@ -375,7 +375,7 @@ function setupDevtoolsPlugin(pluginDescriptor, setupFn) {
375
375
  var activePinia;
376
376
  var setActivePinia = (pinia) => activePinia = pinia;
377
377
  var piniaSymbol = process$1.env.NODE_ENV !== "production" ? Symbol("pinia") : Symbol();
378
- function isPlainObject(o) {
378
+ function isPlainObject$3(o) {
379
379
  return o && typeof o === "object" && Object.prototype.toString.call(o) === "[object Object]" && typeof o.toJSON !== "function";
380
380
  }
381
381
  var MutationType;
@@ -1049,7 +1049,7 @@ function patchObject(newState, oldState) {
1049
1049
  const subPatch = oldState[key];
1050
1050
  if (!(key in newState)) continue;
1051
1051
  const targetValue = newState[key];
1052
- if (isPlainObject(targetValue) && isPlainObject(subPatch) && !(0, lib_exports.isRef)(subPatch) && !(0, lib_exports.isReactive)(subPatch)) newState[key] = patchObject(targetValue, subPatch);
1052
+ if (isPlainObject$3(targetValue) && isPlainObject$3(subPatch) && !(0, lib_exports.isRef)(subPatch) && !(0, lib_exports.isReactive)(subPatch)) newState[key] = patchObject(targetValue, subPatch);
1053
1053
  else newState[key] = subPatch;
1054
1054
  }
1055
1055
  return newState;
@@ -1082,14 +1082,14 @@ function mergeReactiveObjects(target, patchToApply) {
1082
1082
  if (!patchToApply.hasOwnProperty(key)) continue;
1083
1083
  const subPatch = patchToApply[key];
1084
1084
  const targetValue = target[key];
1085
- if (isPlainObject(targetValue) && isPlainObject(subPatch) && target.hasOwnProperty(key) && !(0, lib_exports.isRef)(subPatch) && !(0, lib_exports.isReactive)(subPatch)) target[key] = mergeReactiveObjects(targetValue, subPatch);
1085
+ if (isPlainObject$3(targetValue) && isPlainObject$3(subPatch) && target.hasOwnProperty(key) && !(0, lib_exports.isRef)(subPatch) && !(0, lib_exports.isReactive)(subPatch)) target[key] = mergeReactiveObjects(targetValue, subPatch);
1086
1086
  else target[key] = subPatch;
1087
1087
  }
1088
1088
  return target;
1089
1089
  }
1090
1090
  var skipHydrateSymbol = process$1.env.NODE_ENV !== "production" ? Symbol("pinia:skipHydration") : Symbol();
1091
1091
  function shouldHydrate(obj) {
1092
- return !isPlainObject(obj) || !obj.hasOwnProperty(skipHydrateSymbol);
1092
+ return !isPlainObject$3(obj) || !obj.hasOwnProperty(skipHydrateSymbol);
1093
1093
  }
1094
1094
  var { assign } = Object;
1095
1095
  function isComputed(o) {
@@ -1299,7 +1299,7 @@ function createSetupStore($id, setup, options = {}, pinia, hot, isOptionsStore)
1299
1299
  if (stateKey in store.$state) {
1300
1300
  const newStateTarget = newStore.$state[stateKey];
1301
1301
  const oldStateSource = store.$state[stateKey];
1302
- if (typeof newStateTarget === "object" && isPlainObject(newStateTarget) && isPlainObject(oldStateSource)) patchObject(newStateTarget, oldStateSource);
1302
+ if (typeof newStateTarget === "object" && isPlainObject$3(newStateTarget) && isPlainObject$3(oldStateSource)) patchObject(newStateTarget, oldStateSource);
1303
1303
  else newStore.$state[stateKey] = oldStateSource;
1304
1304
  }
1305
1305
  set(store, stateKey, (0, lib_exports.toRef)(newStore.$state, stateKey));
@@ -3006,6 +3006,11 @@ const useCommentsStore = defineStore("comments", () => {
3006
3006
  ok: false,
3007
3007
  reason: "read-only-document"
3008
3008
  });
3009
+ const resolveDisabledOutcome = () => ({
3010
+ ok: false,
3011
+ reason: "resolve-disabled"
3012
+ });
3013
+ const resolveIsDisabled = () => commentsConfig.allowResolve === false;
3009
3014
  const commentsAreReadOnly$1 = () => commentsConfig.readOnly === true;
3010
3015
  const viewingVisibility = reactive({
3011
3016
  documentMode: "editing",
@@ -4314,6 +4319,7 @@ const useCommentsStore = defineStore("comments", () => {
4314
4319
  };
4315
4320
  const resolveCommentV2 = async ({ superdoc, commentId } = {}) => {
4316
4321
  if (commentsAreReadOnly$1()) return readOnlyMutationOutcome$1();
4322
+ if (resolveIsDisabled()) return resolveDisabledOutcome();
4317
4323
  const v2Adapter = getV2CommentsAdapter(superdoc);
4318
4324
  if (!v2Adapter) return {
4319
4325
  ok: false,
@@ -4361,6 +4367,7 @@ const useCommentsStore = defineStore("comments", () => {
4361
4367
  };
4362
4368
  const reopenCommentV2 = async ({ superdoc, commentId } = {}) => {
4363
4369
  if (commentsAreReadOnly$1()) return readOnlyMutationOutcome$1();
4370
+ if (resolveIsDisabled()) return resolveDisabledOutcome();
4364
4371
  const v2Adapter = getV2CommentsAdapter(superdoc);
4365
4372
  if (!v2Adapter) return {
4366
4373
  ok: false,
@@ -8778,19 +8785,20 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8778
8785
  });
8779
8786
  }
8780
8787
  positions.sort((a, b) => a.anchorTop - b.anchorTop);
8781
- resolveCollisions(positions, activeKey ? positions.findIndex((p) => p.id === activeKey) : -1, 15);
8788
+ const collisionPositions = [];
8782
8789
  for (const position of positions) {
8783
- if (!position.hasAnchorGeometry) continue;
8784
- const offscreenTop = resolvePersistentReviewCardTop({
8790
+ const offscreenTop = position.hasAnchorGeometry ? resolvePersistentReviewCardTop({
8785
8791
  comment: position.commentRef,
8786
8792
  anchorTop: position.anchorTop,
8787
8793
  anchorBottom: position.anchorBottom,
8788
8794
  cardHeight: position.height,
8789
8795
  viewportTop: viewportRange.top,
8790
8796
  viewportBottom: viewportRange.bottom
8791
- });
8792
- if (offscreenTop != null) position.top = offscreenTop;
8797
+ }) : null;
8798
+ if (offscreenTop == null) collisionPositions.push(position);
8799
+ else position.top = offscreenTop;
8793
8800
  }
8801
+ resolveCollisions(collisionPositions, activeKey ? collisionPositions.findIndex((p) => p.id === activeKey) : -1, 15);
8794
8802
  return positions;
8795
8803
  });
8796
8804
  const wrapperMinHeight = computed(() => {
@@ -9298,7 +9306,7 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
9298
9306
  }), 128))], 4)], 36);
9299
9307
  };
9300
9308
  }
9301
- }, [["__scopeId", "data-v-a1bd2282"]]);
9309
+ }, [["__scopeId", "data-v-b5be33eb"]]);
9302
9310
  var _hoisted_1$22 = {
9303
9311
  class: "superdoc__pdf-comments-layer pdf-comments-layer",
9304
9312
  "aria-hidden": "false"
@@ -13850,8 +13858,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
13850
13858
  const allowSelectionInViewMode = () => !!proxy?.$superdoc?.config?.allowSelectionInViewMode;
13851
13859
  const isViewingCommentsVisible = computed(() => isViewingMode() && commentsStore.viewingVisibility.commentsVisible === true);
13852
13860
  const isFindReplaceEnabled = computed(() => {
13853
- const val = proxy?.$superdoc?.config?.modules?.surfaces?.findReplace;
13854
- return val === true || typeof val === "object" && val !== null;
13861
+ return proxy?.$superdoc?.uiConfig?.search?.enabled === true;
13855
13862
  });
13856
13863
  computed(() => isViewingMode() && commentsStore.viewingVisibility.trackChangesVisible === true);
13857
13864
  const shouldRenderCommentsInViewing = computed(() => commentsStore.shouldRenderReviewInViewing);
@@ -13923,7 +13930,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
13923
13930
  getSurfaceManager: getLinkPopoverSurfaceManager,
13924
13931
  getActiveEditor: () => proxy.$superdoc?.activeEditor,
13925
13932
  getUi: getSuperDocUI,
13926
- getResolver: () => proxy.$superdoc?.config?.modules?.links?.popoverResolver,
13933
+ getResolver: () => proxy.$superdoc?.uiConfig?.linkPopover?.suppressed ? () => ({ type: "none" }) : proxy.$superdoc?.config?.modules?.links?.popoverResolver,
13927
13934
  getLayerElement: () => layers.value,
13928
13935
  emitException: (payload) => {
13929
13936
  proxy.$superdoc?.emit("exception", {
@@ -15137,11 +15144,11 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
15137
15144
  externalExtensions: proxy.$superdoc.config.editorExtensions || [],
15138
15145
  extensions: proxy.$superdoc.config.extensions || [],
15139
15146
  suppressDefaultDocxStyles: proxy.$superdoc.config.suppressDefaultDocxStyles,
15140
- disableContextMenu: proxy.$superdoc.config.disableContextMenu,
15147
+ disableContextMenu: !proxy.$superdoc.uiConfig.contextMenu.enabled || proxy.$superdoc.config.disableContextMenu === true,
15141
15148
  jsonOverride: proxy.$superdoc.config.jsonOverride,
15142
15149
  viewOptions: proxy.$superdoc.config.viewOptions,
15143
15150
  contained: proxy.$superdoc.config.contained,
15144
- linkPopoverResolver: proxy.$superdoc.config.modules?.links?.popoverResolver,
15151
+ linkPopoverResolver: proxy.$superdoc.uiConfig.linkPopover.enabled ? proxy.$superdoc.config.modules?.links?.popoverResolver : void 0,
15145
15152
  layoutEngineOptions: useLayoutEngine ? {
15146
15153
  ...proxy.$superdoc.config.layoutEngineOptions || {},
15147
15154
  proofing: resolvedProofingConfig.value,
@@ -15149,7 +15156,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
15149
15156
  zoom: (activeZoom.value ?? 100) / 100,
15150
15157
  emitCommentPositionsInViewing: isViewingMode() && shouldRenderCommentsInViewing.value,
15151
15158
  enableCommentsInViewing: isViewingCommentsVisible.value,
15152
- contentControlsChrome: proxy.$superdoc.config.modules?.contentControls?.chrome,
15159
+ contentControlsChrome: !proxy.$superdoc.uiConfig.contentControls.enabled ? "none" : proxy.$superdoc.uiConfig.contentControls.options.chrome ?? (proxy.$superdoc.config.modules?.contentControls?.chrome === "none" ? void 0 : proxy.$superdoc.config.modules?.contentControls?.chrome),
15153
15160
  resolveTrackedChangeColor: composeAuthorColorResolver(proxy.$superdoc.config.modules?.trackChanges?.authorColors)
15154
15161
  } : void 0,
15155
15162
  permissionResolver: (payload = {}) => proxy.$superdoc.canPerformPermission({
@@ -15744,6 +15751,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
15744
15751
  selectionLayer.value.style.pointerEvents = "none";
15745
15752
  };
15746
15753
  const shouldShowSelection = computed(() => {
15754
+ if (!proxy.$superdoc.uiConfig.comments.enabled) return false;
15747
15755
  const config = proxy.$superdoc.config.modules?.comments;
15748
15756
  if (!config || config === false) return false;
15749
15757
  return !config.readOnly;
@@ -15757,6 +15765,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
15757
15765
  if (!isV2Mode.value) return false;
15758
15766
  if (!doc || doc.type !== "application/vnd.openxmlformats-officedocument.wordprocessingml.document") return false;
15759
15767
  if (proxy.$superdoc.config.viewOptions?.layout === "web") return false;
15768
+ if (proxy.$superdoc.uiConfig.ruler.suppressed) return false;
15760
15769
  if (!Boolean(unwrapDocField(doc.rulers))) return false;
15761
15770
  if (!v2RulerReady.value) return false;
15762
15771
  const editor = proxy.$superdoc?.activeEditor;
@@ -16087,7 +16096,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
16087
16096
  ], 38);
16088
16097
  };
16089
16098
  }
16090
- }, [["__scopeId", "data-v-41f40a40"]]);
16099
+ }, [["__scopeId", "data-v-2a7bfa95"]]);
16091
16100
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
16092
16101
  var PINIA_DEVTOOLS_PLUGIN_ID = "dev.esm.pinia";
16093
16102
  var piniaDevtoolsSuppressionState = {
@@ -28341,7 +28350,7 @@ var SurfaceManager = class {
28341
28350
  if (resolution.type === "external") return { render: resolution.render };
28342
28351
  }
28343
28352
  }
28344
- throw new Error(`SurfaceManager: no renderer resolved for kind "${request.kind}". Provide a resolver via modules.surfaces.resolver, or use a direct-render request.`);
28353
+ throw new Error(`SurfaceManager: no renderer resolved for kind "${request.kind}". Provide a resolver via surfaces.resolver, or use a direct-render request.`);
28345
28354
  }
28346
28355
  #createDestroyedHandle(rawRequest) {
28347
28356
  return {
@@ -28473,6 +28482,129 @@ function normalizeSuperDocUser(user) {
28473
28482
  ...user
28474
28483
  };
28475
28484
  }
28485
+ function mergeDefined(...sources) {
28486
+ const merged = {};
28487
+ for (const source of sources) {
28488
+ if (typeof source !== "object" || source === null) continue;
28489
+ for (const [key, value] of Object.entries(source)) {
28490
+ if (value === void 0) continue;
28491
+ merged[key] = value;
28492
+ }
28493
+ }
28494
+ return merged;
28495
+ }
28496
+ function firstDefined(...values) {
28497
+ for (const value of values) if (value !== void 0) return value;
28498
+ }
28499
+ var HISTORICAL_DEFAULTS = Object.freeze({
28500
+ toolbar: true,
28501
+ comments: true,
28502
+ contextMenu: true,
28503
+ search: false,
28504
+ linkPopover: false,
28505
+ ruler: false,
28506
+ contentControls: true
28507
+ });
28508
+ var DEFAULT_TOOLBAR_GROUP_IDS = Object.freeze([
28509
+ "left",
28510
+ "center",
28511
+ "right"
28512
+ ]);
28513
+ Object.freeze(Object.keys(HISTORICAL_DEFAULTS));
28514
+ var isPlainObject$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28515
+ function resolveSurface(ui, key) {
28516
+ const value = ui?.[key];
28517
+ if (value === false) return false;
28518
+ if (value === void 0) return HISTORICAL_DEFAULTS[key];
28519
+ return true;
28520
+ }
28521
+ function resolveOptions(ui, key) {
28522
+ const value = ui?.[key];
28523
+ return isPlainObject$2(value) ? value : {};
28524
+ }
28525
+ function normalizeUiConfig(config = {}) {
28526
+ const raw = config.ui;
28527
+ const allDisabled = raw === false;
28528
+ const ui = isPlainObject$2(raw) ? raw : void 0;
28529
+ const enabled = (key) => !allDisabled && resolveSurface(ui, key);
28530
+ const explicit = (key) => {
28531
+ if (allDisabled) return false;
28532
+ return ui?.[key] === void 0 ? void 0 : resolveSurface(ui, key);
28533
+ };
28534
+ const options = (key) => allDisabled ? {} : resolveOptions(ui, key);
28535
+ const rulerSuppressed = allDisabled || explicit("ruler") === false;
28536
+ const toolbarOptions = options("toolbar");
28537
+ const legacyToolbar = config.modules?.toolbar;
28538
+ const legacyToolbarOptions = isPlainObject$2(legacyToolbar) ? legacyToolbar : {};
28539
+ const groupCandidates = [toolbarOptions.groups, legacyToolbarOptions.groups];
28540
+ const composedGroups = groupCandidates.find(isPlainObject$2);
28541
+ const selectedGroups = firstDefined(groupCandidates.find(Array.isArray), legacyToolbarOptions.toolbarGroups, config.toolbarGroups);
28542
+ const selection = Array.isArray(selectedGroups) ? selectedGroups : null;
28543
+ const ownGroup = (map, group) => Object.prototype.hasOwnProperty.call(map, group);
28544
+ const projectedGroups = composedGroups && selection ? Object.fromEntries([...selection.filter((group) => ownGroup(composedGroups, group)), ...Object.keys(composedGroups).filter((group) => !DEFAULT_TOOLBAR_GROUP_IDS.includes(group))].map((group) => [group, composedGroups[group]])) : composedGroups;
28545
+ return {
28546
+ enabled: !allDisabled,
28547
+ toolbar: {
28548
+ enabled: enabled("toolbar"),
28549
+ container: allDisabled ? null : firstDefined(toolbarOptions.container, legacyToolbarOptions.selector, config.toolbar) ?? null,
28550
+ options: allDisabled ? {} : {
28551
+ ...mergeDefined(legacyToolbarOptions, toolbarOptions),
28552
+ groups: projectedGroups,
28553
+ toolbarGroups: selectedGroups,
28554
+ icons: mergeDefined(config.toolbarIcons, legacyToolbarOptions.icons, toolbarOptions.icons),
28555
+ texts: mergeDefined(config.toolbarTexts, legacyToolbarOptions.texts, toolbarOptions.texts)
28556
+ }
28557
+ },
28558
+ comments: {
28559
+ enabled: explicit("comments") ?? (!allDisabled && config.modules?.comments !== false),
28560
+ options: options("comments")
28561
+ },
28562
+ contextMenu: {
28563
+ enabled: explicit("contextMenu") ?? (!allDisabled && config.disableContextMenu !== true),
28564
+ suppressed: allDisabled || explicit("contextMenu") === false,
28565
+ options: options("contextMenu")
28566
+ },
28567
+ search: {
28568
+ enabled: explicit("search") ?? (!allDisabled && Boolean(config.modules?.surfaces?.findReplace)),
28569
+ options: options("search")
28570
+ },
28571
+ linkPopover: {
28572
+ enabled: explicit("linkPopover") ?? (!allDisabled && Boolean(config.modules?.links?.popoverResolver)),
28573
+ suppressed: allDisabled || explicit("linkPopover") === false,
28574
+ options: options("linkPopover")
28575
+ },
28576
+ ruler: {
28577
+ enabled: explicit("ruler") ?? (!allDisabled && Boolean(config.rulers)),
28578
+ suppressed: rulerSuppressed,
28579
+ container: rulerSuppressed ? null : options("ruler").container ?? config.rulerContainer ?? null
28580
+ },
28581
+ contentControls: {
28582
+ enabled: explicit("contentControls") ?? (!allDisabled && config.modules?.contentControls?.chrome !== "none"),
28583
+ options: options("contentControls")
28584
+ }
28585
+ };
28586
+ }
28587
+ var isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28588
+ function normalizeInteractionConfig(config = {}) {
28589
+ const interaction = isPlainObject$1(config.interaction) ? config.interaction : {};
28590
+ const interactionComments = isPlainObject$1(interaction.comments) ? interaction.comments : {};
28591
+ const legacyComments = isPlainObject$1(config.modules?.comments) ? config.modules.comments : {};
28592
+ return { comments: {
28593
+ readOnly: (interactionComments.readOnly ?? legacyComments.readOnly ?? false) === true,
28594
+ allowResolve: (interactionComments.allowResolve ?? legacyComments.allowResolve ?? true) !== false
28595
+ } };
28596
+ }
28597
+ var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28598
+ function normalizeSurfacesConfig(config = {}) {
28599
+ const next = isPlainObject(config.surfaces) ? config.surfaces : {};
28600
+ const legacy = isPlainObject(config.modules?.surfaces) ? config.modules.surfaces : {};
28601
+ const resolver = next.resolver === void 0 ? legacy.resolver : next.resolver;
28602
+ return {
28603
+ resolver: typeof resolver === "function" ? resolver : null,
28604
+ dialog: mergeDefined(legacy.dialog, next.dialog),
28605
+ floating: mergeDefined(legacy.floating, next.floating)
28606
+ };
28607
+ }
28476
28608
  var EditorRuntimeRegistry = class {
28477
28609
  #runtimes = /* @__PURE__ */ new Map();
28478
28610
  #rootToId = /* @__PURE__ */ new Map();
@@ -33945,6 +34077,11 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
33945
34077
  this._detachFontsChanged = null;
33946
34078
  }
33947
34079
  #initToolbarGroups() {
34080
+ if (Array.isArray(this.config.groups)) {
34081
+ this.config.toolbarGroups = this.config.groups;
34082
+ this.config.groups = null;
34083
+ return;
34084
+ }
33948
34085
  if (this.config.groups && !Array.isArray(this.config.groups) && Object.keys(this.config.groups).length) this.config.toolbarGroups = Object.keys(this.config.groups);
33949
34086
  }
33950
34087
  getAvailableWidth() {
@@ -33958,6 +34095,7 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
33958
34095
  }
33959
34096
  #makeToolbarItems() {
33960
34097
  const availableWidth = this.getAvailableWidth();
34098
+ const hasExplicitGroupComposition = this.config.groups && !Array.isArray(this.config.groups) && Object.keys(this.config.groups).length > 0;
33961
34099
  const { defaultItems, overflowItems } = makeDefaultItems({
33962
34100
  superToolbar: this,
33963
34101
  toolbarIcons: this.config.icons,
@@ -33972,14 +34110,14 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
33972
34110
  const customToolbarItems = customItems.map((item) => this.#prepareCustomButton(item));
33973
34111
  if (customItems.length) defaultItems.push(...customToolbarItems);
33974
34112
  let allConfigItems = [...defaultItems.map((item) => item.name.value), ...overflowItems.map((item) => item.name.value)];
33975
- if (this.config.groups) {
34113
+ if (hasExplicitGroupComposition) {
33976
34114
  const groupedItems = Object.values(this.config.groups).flatMap((item) => item);
33977
34115
  const configuredGroups = new Set(Object.keys(this.config.groups));
33978
34116
  const groupedCustomItems = customToolbarItems.filter((item) => configuredGroups.has(item.group?.value || "center")).map((item) => item.name.value);
33979
34117
  allConfigItems = [...new Set([...groupedItems, ...groupedCustomItems])];
33980
34118
  }
33981
34119
  const filteredItems = defaultItems.filter((item) => allConfigItems.includes(item.name.value)).filter((item) => !this.config.excludeItems.includes(item.name.value));
33982
- if (this.config.groups) {
34120
+ if (hasExplicitGroupComposition) {
33983
34121
  for (const [group, names] of Object.entries(this.config.groups)) for (const item of filteredItems) if (names.includes(item.name.value)) item.group.value = group;
33984
34122
  }
33985
34123
  this.toolbarItems = filteredItems;
@@ -34543,6 +34681,18 @@ var SuperDoc = class extends import_eventemitter3.default {
34543
34681
  #pendingV2LockSeed = null;
34544
34682
  #mountWrapper = null;
34545
34683
  #ui = null;
34684
+ #uiConfig = normalizeUiConfig({});
34685
+ get uiConfig() {
34686
+ return this.#uiConfig;
34687
+ }
34688
+ #interactionConfig = normalizeInteractionConfig({});
34689
+ get interactionConfig() {
34690
+ return this.#interactionConfig;
34691
+ }
34692
+ #surfacesConfig = normalizeSurfacesConfig({});
34693
+ get surfacesConfig() {
34694
+ return this.#surfacesConfig;
34695
+ }
34546
34696
  #surfaceManager;
34547
34697
  #editorRuntimeRegistry = new EditorRuntimeRegistry();
34548
34698
  #editorRuntimeRegistryUnsub = null;
@@ -34612,7 +34762,7 @@ var SuperDoc = class extends import_eventemitter3.default {
34612
34762
  if (!config.selector) throw new Error("SuperDoc: selector is required");
34613
34763
  const container = typeof config.selector === "string" ? document.querySelector(config.selector) : config.selector;
34614
34764
  if (!(container instanceof HTMLElement)) throw new Error("SuperDoc: selector must be a valid CSS selector string or DOM element");
34615
- this.#surfaceManager = new SurfaceManager({ getModuleConfig: () => this.config.modules?.surfaces });
34765
+ this.#surfaceManager = new SurfaceManager({ getModuleConfig: () => this.#surfacesConfig });
34616
34766
  this.#init(config, container);
34617
34767
  }
34618
34768
  async #init(config, container) {
@@ -34646,10 +34796,21 @@ var SuperDoc = class extends import_eventemitter3.default {
34646
34796
  this.config.modules = this.config.modules || {};
34647
34797
  if (!Object.prototype.hasOwnProperty.call(this.config.modules, "comments")) this.config.modules.comments = {};
34648
34798
  this.config.modules.comments = normalizeCommentsUiPolicy(this.config.modules.comments);
34799
+ this.#uiConfig = normalizeUiConfig(this.config);
34800
+ this.#interactionConfig = normalizeInteractionConfig(this.config);
34801
+ this.#surfacesConfig = normalizeSurfacesConfig(this.config);
34802
+ this.config.disableContextMenu = !this.#uiConfig.contextMenu.enabled;
34803
+ if (!this.#uiConfig.comments.enabled) this.config.modules.comments = false;
34804
+ else if (this.config.modules.comments === false) this.config.modules.comments = {};
34805
+ if (this.#uiConfig.comments.enabled) {
34806
+ if (this.config.modules.comments === true) this.config.modules.comments = {};
34807
+ const commentsBlock = this.config.modules.comments;
34808
+ if (commentsBlock) Object.assign(commentsBlock, this.#interactionConfig.comments);
34809
+ }
34649
34810
  this.config.colors = shuffleArray(this.config.colors);
34650
34811
  this.userColorMap = /* @__PURE__ */ new Map();
34651
34812
  this.colorIndex = 0;
34652
- this.version = "2.0.0-next.49";
34813
+ this.version = "2.0.0-next.50";
34653
34814
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
34654
34815
  this.superdocId = config.superdocId || v4_default();
34655
34816
  this.colors = this.config.colors ?? [];
@@ -35661,29 +35822,28 @@ var SuperDoc = class extends import_eventemitter3.default {
35661
35822
  #addToolbar() {
35662
35823
  const toolbarModuleConfig = this.config.modules?.toolbar;
35663
35824
  const toolbarModule = toolbarModuleConfig && typeof toolbarModuleConfig === "object" ? toolbarModuleConfig : {};
35664
- this.toolbarElement = toolbarModule.selector || this.config.toolbar;
35665
- if (!(Boolean(this.config.toolbar) || toolbarModuleConfig === true || toolbarModuleConfig != null && typeof toolbarModuleConfig === "object")) {
35825
+ const toolbarUi = this.#uiConfig.toolbar;
35826
+ this.toolbarElement = toolbarUi.container ?? void 0;
35827
+ const uiBlock = this.config.ui;
35828
+ const namedInUiBlock = typeof uiBlock === "object" && uiBlock !== null && uiBlock.toolbar !== void 0;
35829
+ if (!(toolbarUi.enabled && (Boolean(toolbarUi.container) || toolbarModuleConfig === true || toolbarModuleConfig != null && typeof toolbarModuleConfig === "object" || namedInUiBlock))) {
35666
35830
  this.toolbar = null;
35667
35831
  return;
35668
35832
  }
35669
35833
  try {
35670
35834
  this.toolbar = createBuiltInToolbar({
35671
35835
  ...toolbarModule,
35836
+ ...toolbarUi.options,
35672
35837
  selector: this.toolbarElement,
35673
35838
  superdoc: this,
35674
35839
  editor: this.activeEditor,
35675
35840
  role: this.config.role,
35676
35841
  isDev: this.isDev,
35677
35842
  documentMode: this.config.documentMode,
35678
- toolbarGroups: toolbarModule.toolbarGroups ?? this.config.toolbarGroups,
35679
- icons: {
35680
- ...this.config.toolbarIcons,
35681
- ...toolbarModule.icons
35682
- },
35683
- texts: {
35684
- ...this.config.toolbarTexts,
35685
- ...toolbarModule.texts
35686
- },
35843
+ groups: toolbarUi.options.groups,
35844
+ toolbarGroups: toolbarUi.options.toolbarGroups,
35845
+ icons: toolbarUi.options.icons,
35846
+ texts: toolbarUi.options.texts,
35687
35847
  uiDisplayFallbackFont: this.config.uiDisplayFallbackFont
35688
35848
  });
35689
35849
  } catch (error) {
@@ -35734,6 +35894,7 @@ var SuperDoc = class extends import_eventemitter3.default {
35734
35894
  return documentRuntime.scrollToElement(elementId);
35735
35895
  }
35736
35896
  setDisableContextMenu(disabled = true) {
35897
+ if (!disabled && this.#uiConfig.contextMenu.suppressed) return;
35737
35898
  const nextValue = Boolean(disabled);
35738
35899
  if (this.config.disableContextMenu === nextValue) return;
35739
35900
  this.config.disableContextMenu = nextValue;
@@ -36017,13 +36178,26 @@ var SuperDoc = class extends import_eventemitter3.default {
36017
36178
  if (isV2ActiveEditorFacade(activeEditor) && typeof activeEditor.replaceFile === "function") {
36018
36179
  const result = await activeEditor.replaceFile(source);
36019
36180
  const state = result && typeof result === "object" ? result.state : null;
36020
- if (state === null || state === "review-ready" || state === "editing-ready") this.#replaceActiveDocumentData(activeEditor, source);
36181
+ if (state === null || state === "review-ready" || state === "editing-ready") {
36182
+ this.#replaceActiveDocumentData(activeEditor, source);
36183
+ this.emit("document-replaced", {
36184
+ editor: activeEditor,
36185
+ host: activeEditor?.host
36186
+ });
36187
+ }
36021
36188
  return result;
36022
36189
  }
36023
36190
  const legacyReplaceFile = activeEditor && !isV2ActiveEditorFacade(activeEditor) ? activeEditor.replaceFile : null;
36024
36191
  if (typeof legacyReplaceFile === "function") {
36025
36192
  const result = await legacyReplaceFile.call(activeEditor, source);
36026
- this.#replaceActiveDocumentData(activeEditor, source);
36193
+ const legacyState = result && typeof result === "object" ? result.state : void 0;
36194
+ if (legacyState === void 0 || legacyState === null || legacyState === "review-ready" || legacyState === "editing-ready") {
36195
+ this.#replaceActiveDocumentData(activeEditor, source);
36196
+ this.emit("document-replaced", {
36197
+ editor: activeEditor,
36198
+ host: activeEditor?.host
36199
+ });
36200
+ }
36027
36201
  return result;
36028
36202
  }
36029
36203
  throw new Error("SuperDoc: replaceFile is unavailable for the active editor");