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
@@ -1368,28 +1368,28 @@ img[data-v-c95b2073] {
1368
1368
  background-color: #1355ff7f;
1369
1369
  }
1370
1370
 
1371
- .comment-placeholder[data-v-a1bd2282] {
1371
+ .comment-placeholder[data-v-b5be33eb] {
1372
1372
  position: absolute;
1373
1373
  width: 300px;
1374
1374
  transition: top 0.3s ease;
1375
1375
  }
1376
- .comment-placeholder.is-direct-decision-continuity-target[data-v-a1bd2282] .overflow-menu {
1376
+ .comment-placeholder.is-direct-decision-continuity-target[data-v-b5be33eb] .overflow-menu {
1377
1377
  opacity: 1;
1378
1378
  pointer-events: auto;
1379
1379
  }
1380
- .floating-comment[data-v-a1bd2282] {
1380
+ .floating-comment[data-v-b5be33eb] {
1381
1381
  position: relative;
1382
1382
  display: block;
1383
1383
  min-width: 300px;
1384
1384
  }
1385
- .sidebar-container[data-v-a1bd2282] {
1385
+ .sidebar-container[data-v-b5be33eb] {
1386
1386
  position: absolute;
1387
1387
  width: 300px;
1388
1388
  min-height: 300px;
1389
1389
  transition: transform 0.3s ease;
1390
1390
  will-change: transform;
1391
1391
  }
1392
- .section-wrapper[data-v-a1bd2282] {
1392
+ .section-wrapper[data-v-b5be33eb] {
1393
1393
  position: relative;
1394
1394
  min-height: 100%;
1395
1395
  width: 300px;
@@ -1727,11 +1727,11 @@ img[data-v-c95b2073] {
1727
1727
  pointer-events: auto;
1728
1728
  }
1729
1729
 
1730
- .superdoc[data-v-41f40a40] {
1730
+ .superdoc[data-v-2a7bfa95] {
1731
1731
  display: flex;
1732
1732
  position: relative;
1733
1733
  }
1734
- .sd-visually-hidden[data-v-41f40a40] {
1734
+ .sd-visually-hidden[data-v-2a7bfa95] {
1735
1735
  position: absolute;
1736
1736
  width: 1px;
1737
1737
  height: 1px;
@@ -1742,30 +1742,30 @@ img[data-v-c95b2073] {
1742
1742
  white-space: nowrap;
1743
1743
  border: 0;
1744
1744
  }
1745
- .right-sidebar[data-v-41f40a40] {
1745
+ .right-sidebar[data-v-2a7bfa95] {
1746
1746
  min-width: 320px;
1747
1747
  height: 100%;
1748
1748
  }
1749
- .floating-comments[data-v-41f40a40] {
1749
+ .floating-comments[data-v-2a7bfa95] {
1750
1750
  min-width: 300px;
1751
1751
  width: 300px;
1752
1752
  height: 100%;
1753
1753
  overflow: visible;
1754
1754
  }
1755
- .superdoc__layers[data-v-41f40a40] {
1755
+ .superdoc__layers[data-v-2a7bfa95] {
1756
1756
  height: 100%;
1757
1757
  position: relative;
1758
1758
  box-sizing: border-box;
1759
1759
  }
1760
- .superdoc__document[data-v-41f40a40] {
1760
+ .superdoc__document[data-v-2a7bfa95] {
1761
1761
  width: 100%;
1762
1762
  position: relative;
1763
1763
  }
1764
- .superdoc__sub-document[data-v-41f40a40] {
1764
+ .superdoc__sub-document[data-v-2a7bfa95] {
1765
1765
  width: 100%;
1766
1766
  position: relative;
1767
1767
  }
1768
- .superdoc__selection-layer[data-v-41f40a40] {
1768
+ .superdoc__selection-layer[data-v-2a7bfa95] {
1769
1769
  position: absolute;
1770
1770
  min-width: 100%;
1771
1771
  min-height: 100%;
@@ -1776,13 +1776,13 @@ img[data-v-c95b2073] {
1776
1776
  /* SD-3497: PDF whiteboard overlay sits above the rendered PDF canvas but below
1777
1777
  the PDF comment anchors (z-index 6 in PdfCommentsLayer) so anchors stay
1778
1778
  clickable, and below the selection layer (z-index 10). */
1779
- .superdoc__whiteboard-layer[data-v-41f40a40] {
1779
+ .superdoc__whiteboard-layer[data-v-2a7bfa95] {
1780
1780
  z-index: 4;
1781
1781
  }
1782
- .superdoc__temp-selection[data-v-41f40a40] {
1782
+ .superdoc__temp-selection[data-v-2a7bfa95] {
1783
1783
  position: absolute;
1784
1784
  }
1785
- .superdoc__right-sidebar[data-v-41f40a40] {
1785
+ .superdoc__right-sidebar[data-v-2a7bfa95] {
1786
1786
  width: 320px;
1787
1787
  min-width: 320px;
1788
1788
  padding: 0 10px;
@@ -1790,7 +1790,7 @@ img[data-v-c95b2073] {
1790
1790
  position: relative;
1791
1791
  z-index: 2;
1792
1792
  }
1793
- .superdoc__compact-comment-popover[data-v-41f40a40] {
1793
+ .superdoc__compact-comment-popover[data-v-2a7bfa95] {
1794
1794
  position: absolute;
1795
1795
  top: 12px;
1796
1796
  right: 12px;
@@ -1799,14 +1799,14 @@ img[data-v-c95b2073] {
1799
1799
  }
1800
1800
 
1801
1801
  /* Tools styles */
1802
- .tools[data-v-41f40a40] {
1802
+ .tools[data-v-2a7bfa95] {
1803
1803
  position: absolute;
1804
1804
  z-index: 3;
1805
1805
  display: flex;
1806
1806
  flex-direction: column;
1807
1807
  gap: var(--sd-ui-tools-gap, 6px);
1808
1808
  }
1809
- .tools-item[data-v-41f40a40] {
1809
+ .tools-item[data-v-2a7bfa95] {
1810
1810
  display: flex;
1811
1811
  align-items: center;
1812
1812
  justify-content: center;
@@ -1817,10 +1817,10 @@ img[data-v-c95b2073] {
1817
1817
  cursor: pointer;
1818
1818
  position: relative;
1819
1819
  }
1820
- .tools-item i[data-v-41f40a40] {
1820
+ .tools-item i[data-v-2a7bfa95] {
1821
1821
  cursor: pointer;
1822
1822
  }
1823
- .superdoc__tools-icon[data-v-41f40a40] {
1823
+ .superdoc__tools-icon[data-v-2a7bfa95] {
1824
1824
  width: var(--sd-ui-tools-icon-size, 20px);
1825
1825
  height: var(--sd-ui-tools-icon-size, 20px);
1826
1826
  flex-shrink: 0;
@@ -1835,22 +1835,22 @@ img[data-v-c95b2073] {
1835
1835
 
1836
1836
  /* 834px is iPad screen size in portrait orientation */
1837
1837
  @media (max-width: 834px) {
1838
- .superdoc .superdoc__layers[data-v-41f40a40] {
1838
+ .superdoc .superdoc__layers[data-v-2a7bfa95] {
1839
1839
  margin: 0;
1840
1840
  border: 0 !important;
1841
1841
  box-shadow: none;
1842
1842
  }
1843
- .superdoc__sub-document[data-v-41f40a40] {
1843
+ .superdoc__sub-document[data-v-2a7bfa95] {
1844
1844
  max-width: 100%;
1845
1845
  }
1846
- .superdoc__right-sidebar[data-v-41f40a40] {
1846
+ .superdoc__right-sidebar[data-v-2a7bfa95] {
1847
1847
  padding: 10px;
1848
1848
  position: relative;
1849
1849
  }
1850
1850
  }
1851
1851
 
1852
1852
  /* AI Writer styles */
1853
- .ai-writer-container[data-v-41f40a40] {
1853
+ .ai-writer-container[data-v-2a7bfa95] {
1854
1854
  position: fixed;
1855
1855
  z-index: 1000;
1856
1856
  background: white;
@@ -1866,10 +1866,10 @@ img[data-v-c95b2073] {
1866
1866
  transform: translateY(-50%);
1867
1867
  z-index: 50;
1868
1868
  } */
1869
- .ai-tool > svg[data-v-41f40a40] {
1869
+ .ai-tool > svg[data-v-2a7bfa95] {
1870
1870
  fill: transparent;
1871
1871
  }
1872
- .ai-tool[data-v-41f40a40]::before {
1872
+ .ai-tool[data-v-2a7bfa95]::before {
1873
1873
  content: '';
1874
1874
  position: absolute;
1875
1875
  width: 20px;
@@ -1890,7 +1890,7 @@ img[data-v-c95b2073] {
1890
1890
  filter: brightness(1.2);
1891
1891
  transition: filter 0.2s ease;
1892
1892
  }
1893
- .ai-tool[data-v-41f40a40]:hover::before {
1893
+ .ai-tool[data-v-2a7bfa95]:hover::before {
1894
1894
  filter: brightness(1.3);
1895
1895
  }
1896
1896
 
@@ -2,9 +2,12 @@ import { EventEmitter } from 'eventemitter3';
2
2
  import { HocuspocusProviderWebsocket } from '@hocuspocus/provider';
3
3
  import { createSuperdocVueApp } from './create-app.js';
4
4
  import { Whiteboard } from './whiteboard/Whiteboard.js';
5
+ import { normalizeUiConfig } from './config/normalize-ui-config.js';
6
+ import { normalizeInteractionConfig } from './config/normalize-interaction-config.js';
7
+ import { normalizeSurfacesConfig } from './config/normalize-surfaces-config.js';
5
8
  import { EditorRuntimeFocusOptions } from './editor-runtime/types.js';
6
9
  import { BorrowedSuperDocUI } from '../public/ui/types.js';
7
- import { AwarenessUser, CanPerformPermissionParams, CollaborationProvider, Config, ContentControlActiveChangePayload, ContentControlClickPayload, DocumentMode, Editor, EditorUpdateEvent, ExportParams, FontsChangedPayload, FontsResolvedPayload, InternalConfig, ListDefinitionsPayload, NavigableAddress, DocumentRendererRuntime, SearchMatch, SuperDocAwarenessUpdatePayload, SuperDocCommentsUpdatePayload, SuperDocEditorPayload, SuperDocExceptionPayload, SuperDocFontsApi, SuperDocLockedPayload, SuperDocMeasurementUnit, SuperDocMeasurementUnitChangePayload, SuperDocReadyPayload, SuperDocState, SuperDocViewportChangePayload, SuperDocViewportMetrics, SuperDocZoomMode, SuperDocZoomPayload, SuperDocZoomState, SurfaceHandle, SurfaceRequest, UpgradeToCollaborationOptions, User } from './types/index.js';
10
+ import { AwarenessUser, CanPerformPermissionParams, CollaborationProvider, Config, ContentControlActiveChangePayload, ContentControlClickPayload, DocumentMode, Editor, EditorUpdateEvent, ExportParams, FontsChangedPayload, FontsResolvedPayload, InternalConfig, ListDefinitionsPayload, NavigableAddress, DocumentRendererRuntime, SearchMatch, SuperDocAwarenessUpdatePayload, SuperDocCommentsUpdatePayload, SuperDocDocumentReplacedPayload, SuperDocEditorPayload, SuperDocExceptionPayload, SuperDocFontsApi, SuperDocLockedPayload, SuperDocMeasurementUnit, SuperDocMeasurementUnitChangePayload, SuperDocReadyPayload, SuperDocState, SuperDocViewportChangePayload, SuperDocViewportMetrics, SuperDocZoomMode, SuperDocZoomPayload, SuperDocZoomState, SurfaceHandle, SurfaceRequest, UpgradeToCollaborationOptions, User } from './types/index.js';
8
11
  import { WhiteboardData } from './whiteboard/Whiteboard.js';
9
12
  type ToolbarLike = {
10
13
  activeEditor?: unknown;
@@ -61,6 +64,21 @@ interface SuperDocEventMap {
61
64
  * neither a pre-ready read nor a clear would refresh without this.
62
65
  */
63
66
  'active-editor-change': [];
67
+ /**
68
+ * `replaceFile()` swapped the content under a stable editor identity.
69
+ *
70
+ * Deliberately distinct from `active-editor-change`: the editor object and its
71
+ * host both survive a replace, so anything bound to the HOST — geometry
72
+ * observers, for one — is still attached to the thing now rendering the
73
+ * replacement and must not be torn down. Only state describing the previous
74
+ * document's content is stale.
75
+ *
76
+ * Emitted only after the replacement is confirmed, and carrying the editor
77
+ * whose replacement completed: a replace is asynchronous, so the active editor
78
+ * can move while it is in flight, and a consumer must ignore an event naming an
79
+ * editor it is not bound to.
80
+ */
81
+ 'document-replaced': [SuperDocDocumentReplacedPayload];
64
82
  'editor-update': [EditorUpdateEvent];
65
83
  'content-error': [SuperDocContentErrorPayload];
66
84
  'fonts-resolved': [FontsResolvedPayload];
@@ -92,6 +110,30 @@ interface SuperDocEventMap {
92
110
  export declare class SuperDoc extends EventEmitter<SuperDocEventMap> {
93
111
  #private;
94
112
  static allowedTypes: ("application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "application/pdf" | "text/html")[];
113
+ /**
114
+ * Which built-in surfaces this instance renders.
115
+ *
116
+ * Resolved from {@link Config.ui}, falling back to the historical defaults
117
+ * when it is omitted. Read-only: changing what SuperDoc renders after mount
118
+ * is a per-surface concern, not a config swap.
119
+ */
120
+ get uiConfig(): ReturnType<typeof normalizeUiConfig>;
121
+ /**
122
+ * What the user is permitted to do, as distinct from what SuperDoc draws.
123
+ *
124
+ * Resolved from {@link Config.interaction}. Stays meaningful when the
125
+ * application renders its own UI: `ui: false` removes the built-in comment
126
+ * dialog but not the policy that rejects a mutation.
127
+ */
128
+ get interactionConfig(): ReturnType<typeof normalizeInteractionConfig>;
129
+ /**
130
+ * Shared plumbing for dialogs and floating overlays, including ones the
131
+ * application opens itself through `openSurface()`.
132
+ *
133
+ * Resolved from {@link Config.surfaces}. Unaffected by `ui: false`, which
134
+ * turns off SuperDoc's own surfaces without disabling the mechanism.
135
+ */
136
+ get surfacesConfig(): ReturnType<typeof normalizeSurfacesConfig>;
95
137
  /**
96
138
  * Build-time SuperDoc version string. Initialized to `'0.0.0'` so the
97
139
  * field is structurally assigned before the constructor runs, then
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Merge config layers without letting an explicit `undefined` erase a value.
3
+ *
4
+ * Object spread treats a present-but-undefined key as a real assignment, so
5
+ * `{ ...legacy, ...next }` clobbers `legacy.closeOnEscape` whenever `next`
6
+ * carries `closeOnEscape: undefined`. That shape is common and unintentional:
7
+ * configs assembled from optional properties, spreads, or destructured
8
+ * arguments routinely carry keys whose value is undefined without the consumer
9
+ * meaning anything by it.
10
+ *
11
+ * Everywhere this migration merges a new spelling over a legacy one, the rule
12
+ * is the same as the single-value case: `undefined` means "unset, fall
13
+ * through", and only an explicit value overrides.
14
+ */
15
+ /**
16
+ * Merge sources left to right, skipping keys whose value is `undefined`.
17
+ *
18
+ * Later sources win, as with spread. Non-object sources are ignored so callers
19
+ * can pass a possibly-absent block without guarding first.
20
+ *
21
+ * @param {...(Record<string, unknown> | undefined)} sources
22
+ * @returns {Record<string, unknown>}
23
+ */
24
+ export function mergeDefined(...sources: (Record<string, unknown> | undefined)[]): Record<string, unknown>;
25
+ /**
26
+ * Pick the first defined value, so `undefined` falls through but `null`,
27
+ * `false`, and `0` are honored as deliberate choices.
28
+ *
29
+ * `??` already does this for null and undefined together, which is wrong when
30
+ * `null` carries meaning — clearing an inherited resolver, for instance.
31
+ *
32
+ * @param {...unknown} values
33
+ * @returns {unknown}
34
+ */
35
+ export function firstDefined(...values: unknown[]): unknown;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Resolve the effective interaction policy.
3
+ *
4
+ * Reads `interaction.comments` first, then the legacy fields on
5
+ * `modules.comments`, so both spellings work while the migration lands.
6
+ *
7
+ * @param {Record<string, any>} [config] Raw consumer config.
8
+ * @returns {{ comments: { readOnly: boolean, allowResolve: boolean } }}
9
+ */
10
+ export function normalizeInteractionConfig(config?: Record<string, any>): {
11
+ comments: {
12
+ readOnly: boolean;
13
+ allowResolve: boolean;
14
+ };
15
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Resolve the generic surface infrastructure config.
3
+ *
4
+ * Reads top-level `surfaces` first, then the legacy `modules.surfaces` block,
5
+ * so both spellings work while the migration lands. Built-in surface intents
6
+ * are deliberately not read here; they belong to the UI profile.
7
+ *
8
+ * The resolved shape mirrors the public `SurfacesConfig`, with every member
9
+ * present: the presets are merged objects rather than optional, and `resolver`
10
+ * is a function or `null` once a non-function value has been rejected.
11
+ *
12
+ * @param {Record<string, any>} [config] Raw consumer config.
13
+ * @returns {{
14
+ * resolver: import('../types/index.js').SurfaceResolver | null,
15
+ * dialog: NonNullable<import('../types/index.js').SurfacesConfig['dialog']>,
16
+ * floating: NonNullable<import('../types/index.js').SurfacesConfig['floating']>,
17
+ * }}
18
+ */
19
+ export function normalizeSurfacesConfig(config?: Record<string, any>): {
20
+ resolver: import('../types/index.js').SurfaceResolver | null;
21
+ dialog: NonNullable<import('../types/index.js').SurfacesConfig["dialog"]>;
22
+ floating: NonNullable<import('../types/index.js').SurfacesConfig["floating"]>;
23
+ };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Collapse a consumer config into the effective built-in UI profile.
3
+ *
4
+ * @param {Record<string, any>} [config] Raw consumer config.
5
+ * @returns {{
6
+ * enabled: boolean,
7
+ * toolbar: { enabled: boolean, container: string | HTMLElement | null, options: Record<string, unknown> },
8
+ * comments: { enabled: boolean, options: Record<string, unknown> },
9
+ * contextMenu: { enabled: boolean, suppressed: boolean, options: Record<string, unknown> },
10
+ * search: { enabled: boolean, options: Record<string, unknown> },
11
+ * linkPopover: { enabled: boolean, suppressed: boolean, options: Record<string, unknown> },
12
+ * ruler: { enabled: boolean, suppressed: boolean, container: string | HTMLElement | null },
13
+ * contentControls: { enabled: boolean, options: Record<string, unknown> },
14
+ * }}
15
+ */
16
+ export function normalizeUiConfig(config?: Record<string, any>): {
17
+ enabled: boolean;
18
+ toolbar: {
19
+ enabled: boolean;
20
+ container: string | HTMLElement | null;
21
+ options: Record<string, unknown>;
22
+ };
23
+ comments: {
24
+ enabled: boolean;
25
+ options: Record<string, unknown>;
26
+ };
27
+ contextMenu: {
28
+ enabled: boolean;
29
+ suppressed: boolean;
30
+ options: Record<string, unknown>;
31
+ };
32
+ search: {
33
+ enabled: boolean;
34
+ options: Record<string, unknown>;
35
+ };
36
+ linkPopover: {
37
+ enabled: boolean;
38
+ suppressed: boolean;
39
+ options: Record<string, unknown>;
40
+ };
41
+ ruler: {
42
+ enabled: boolean;
43
+ suppressed: boolean;
44
+ container: string | HTMLElement | null;
45
+ };
46
+ contentControls: {
47
+ enabled: boolean;
48
+ options: Record<string, unknown>;
49
+ };
50
+ };
51
+ /** Every built-in surface `ui: false` turns off. */
52
+ export const BUILT_IN_SURFACES: readonly string[];
53
+ /** Surfaces that render when the consumer says nothing at all. */
54
+ export const HISTORICAL_DEFAULTS: Readonly<{
55
+ toolbar: true;
56
+ comments: true;
57
+ contextMenu: true;
58
+ search: false;
59
+ linkPopover: false;
60
+ ruler: false;
61
+ contentControls: true;
62
+ }>;
@@ -995,8 +995,14 @@ export interface ExternalSurfaceRenderContext {
995
995
  }
996
996
  /** Module-level configuration for the surface system. */
997
997
  export interface SurfacesModuleConfig {
998
- /** Global surface resolver. */
999
- resolver?: SurfaceResolver;
998
+ /**
999
+ * Global surface resolver.
1000
+ *
1001
+ * `null` is the resolved "no resolver" value the normalizer produces after
1002
+ * rejecting a non-function; SurfaceManager guards with `typeof === 'function'`
1003
+ * either way.
1004
+ */
1005
+ resolver?: SurfaceResolver | null;
1000
1006
  /** Default dialog options. */
1001
1007
  dialog?: {
1002
1008
  /** Default escape behavior for dialogs (default: true). */
@@ -1961,6 +1967,32 @@ export interface SuperDocReadyPayload {
1961
1967
  export interface SuperDocEditorPayload {
1962
1968
  editor: Editor;
1963
1969
  }
1970
+ /**
1971
+ * Payload emitted with `document-replaced`.
1972
+ *
1973
+ * `editor` is the editor whose replacement completed, not necessarily the one
1974
+ * active when the event is received: a replace is asynchronous, so the active
1975
+ * editor can move while it is in flight. A consumer must compare this against
1976
+ * the editor it is bound to and ignore anything else.
1977
+ *
1978
+ * Typed `unknown` rather than `Editor`: the active editor can be a v2 facade
1979
+ * that does not satisfy `Editor` (`getHTML` is required there and absent on the
1980
+ * facade), so annotating it as `Editor` would promise methods that are not
1981
+ * present. It is an identity token to compare, not an object to call.
1982
+ */
1983
+ export interface SuperDocDocumentReplacedPayload {
1984
+ editor: unknown;
1985
+ /**
1986
+ * The host that rendered the replaced document.
1987
+ *
1988
+ * Carried because `editor` alone cannot be matched in the V2 browser path: a
1989
+ * successful replace emits its ready payload before `replaceFile()` resolves,
1990
+ * so the shell has already installed a NEW facade by the time this event
1991
+ * fires, and the captured facade is one the controller no longer holds. The
1992
+ * host survives that swap, so it is the identity that still lines up.
1993
+ */
1994
+ host: unknown;
1995
+ }
1964
1996
  /**
1965
1997
  * Payload emitted with the `locked` event and passed to
1966
1998
  * `Config.onLocked`. `lockedBy` is non-optional because the runtime
@@ -2333,6 +2365,134 @@ export interface SuperDocZoomConfig {
2333
2365
  /** Bounds and padding for the `fit-width` policy. */
2334
2366
  fitWidth?: SuperDocFitWidthOptions;
2335
2367
  }
2368
+ /**
2369
+ * Per-surface built-in UI configuration. Every field is optional; an omitted
2370
+ * field keeps that surface's historical default rather than inheriting from
2371
+ * its siblings, so a partial config only changes what it names.
2372
+ *
2373
+ * `false` disables a surface, `true` enables it with defaults, and an options
2374
+ * object both enables and configures it.
2375
+ *
2376
+ * @see {@link Config.ui}
2377
+ */
2378
+ export interface UIConfig {
2379
+ /**
2380
+ * Built-in toolbar. Enabled by default, but a toolbar only appears once
2381
+ * `container` resolves to an element — enabling it without one creates the
2382
+ * `superdoc.toolbar` handle and renders nothing.
2383
+ */
2384
+ toolbar?: boolean | {
2385
+ /**
2386
+ * Where to render the toolbar: an element, an id selector
2387
+ * (`#toolbar`), a class selector (`.toolbar`), or a bare element id.
2388
+ * Other CSS selector syntax resolves to nothing.
2389
+ */
2390
+ container?: string | HTMLElement;
2391
+ /**
2392
+ * Which groups render, or what goes in them. The shape decides which
2393
+ * of the two it means, so both v1 spellings have somewhere to land.
2394
+ *
2395
+ * An array selects which groups render, e.g.
2396
+ * `['left', 'center', 'right']`. This is where `Config.toolbarGroups`
2397
+ * moves to. It is a membership list, not a sort order: the built-in
2398
+ * toolbar lays groups out left, center, right, and renders center
2399
+ * whether or not it is listed.
2400
+ *
2401
+ * An object is composition: a group id mapped to the item ids inside
2402
+ * it, e.g. `{ right: ['bold'] }`. This is where
2403
+ * `modules.toolbar.groups` moves to. Supplying both an ordering array
2404
+ * here and a legacy composition map keeps the composition and applies
2405
+ * the ordering as a filter.
2406
+ */
2407
+ groups?: string[] | Record<string, string[]>;
2408
+ /** Toolbar item ids to hide from the default set. */
2409
+ excludeItems?: string[];
2410
+ /** Icon overrides, merged over the built-in set. */
2411
+ icons?: Record<string, unknown>;
2412
+ /** Text overrides, merged over the built-in set. */
2413
+ texts?: Record<string, unknown>;
2414
+ /** Size the toolbar to its container rather than the viewport. */
2415
+ responsiveToContainer?: boolean;
2416
+ };
2417
+ /** Built-in comments UI. Enabled by default. */
2418
+ comments?: boolean | Record<string, unknown>;
2419
+ /** Built-in right-click and slash context menu. Enabled by default. */
2420
+ contextMenu?: boolean | Record<string, unknown>;
2421
+ /**
2422
+ * Built-in find/replace surface. Disabled by default. Enabling it lets
2423
+ * SuperDoc intercept Cmd+F / Ctrl+F; `editor.ui.search` stays available to
2424
+ * custom UI either way.
2425
+ */
2426
+ search?: boolean | Record<string, unknown>;
2427
+ /** Built-in popover shown when a link is selected. Disabled by default. */
2428
+ linkPopover?: boolean | Record<string, unknown>;
2429
+ /** Built-in ruler. Disabled by default. */
2430
+ ruler?: boolean | {
2431
+ /** Element or selector to render the ruler into. */
2432
+ container?: string | HTMLElement;
2433
+ };
2434
+ /** Built-in chrome drawn around content controls. Enabled by default. */
2435
+ contentControls?: boolean | Record<string, unknown>;
2436
+ }
2437
+ /**
2438
+ * What the user is permitted to do, as distinct from what SuperDoc draws.
2439
+ *
2440
+ * Policy outlives presentation: `ui: false` removes the built-in comment
2441
+ * dialog, but an application rendering its own still needs `readOnly`
2442
+ * enforced. Keeping the two apart means a custom UI does not have to hold a
2443
+ * `modules.comments` object alive purely to carry policy.
2444
+ */
2445
+ export interface InteractionConfig {
2446
+ /** Comment and tracked-change interaction policy. */
2447
+ comments?: {
2448
+ /** Reject every comment and tracked-change mutation (default: false). */
2449
+ readOnly?: boolean;
2450
+ /** Offer resolve/reopen actions when writable (default: true). */
2451
+ allowResolve?: boolean;
2452
+ };
2453
+ }
2454
+ /**
2455
+ * Shared plumbing for dialogs and floating overlays, including surfaces the
2456
+ * application opens itself through `superdoc.openSurface()`.
2457
+ *
2458
+ * Unaffected by `ui: false`: turning off SuperDoc's own surfaces does not
2459
+ * disable the mechanism an application uses to render its own.
2460
+ */
2461
+ export interface SurfacesConfig {
2462
+ /**
2463
+ * Resolver for intent-based surface requests.
2464
+ *
2465
+ * `null` explicitly clears a resolver inherited from the legacy
2466
+ * `modules.surfaces.resolver`, which omitting the key does not do.
2467
+ */
2468
+ resolver?: SurfaceResolver | null;
2469
+ /** Defaults applied to dialog surfaces. */
2470
+ dialog?: {
2471
+ /** Close on Escape (default: true). */
2472
+ closeOnEscape?: boolean;
2473
+ /** Close on backdrop click (default: true). */
2474
+ closeOnBackdrop?: boolean;
2475
+ /** Default max width. */
2476
+ maxWidth?: string | number;
2477
+ };
2478
+ /** Defaults applied to floating surfaces. */
2479
+ floating?: {
2480
+ /** Placement preset (default: 'top-right'). */
2481
+ placement?: SurfaceFloatingPlacement;
2482
+ /** Default width. */
2483
+ width?: string | number;
2484
+ /** Default max width. */
2485
+ maxWidth?: string | number;
2486
+ /** Default max height. */
2487
+ maxHeight?: string | number;
2488
+ /** Close on Escape (default: true). */
2489
+ closeOnEscape?: boolean;
2490
+ /** Close on outside pointer down (default: false). */
2491
+ closeOnOutsidePointerDown?: boolean;
2492
+ /** Focus the surface on open (default: true). */
2493
+ autoFocus?: boolean;
2494
+ };
2495
+ }
2336
2496
  export interface Config {
2337
2497
  /** The ID of the SuperDoc. */
2338
2498
  superdocId?: string;
@@ -2380,6 +2540,53 @@ export interface Config {
2380
2540
  users?: User[];
2381
2541
  /** Colors to use for user awareness. */
2382
2542
  colors?: string[];
2543
+ /**
2544
+ * Which built-in interface SuperDoc renders.
2545
+ *
2546
+ * Omit it to keep SuperDoc's historical rendering: comments, the context
2547
+ * menu, and content-control chrome are on; search, the link popover, and
2548
+ * the ruler are opt-in; and the toolbar renders once it has somewhere to
2549
+ * mount. That profile is not symmetrical, and omitting this field
2550
+ * reproduces it exactly.
2551
+ *
2552
+ * Pass `false` when the application owns the interface. SuperDoc then
2553
+ * renders no controls, chrome, dialogs, or popovers, while the document,
2554
+ * the Document API, and `editor.ui` keep working — so a custom UI drives
2555
+ * the same commands the built-in one would have.
2556
+ *
2557
+ * Pass an object to choose per surface. An omitted key keeps that
2558
+ * surface's default rather than following its siblings, so
2559
+ * `{ comments: false }` disables comments and changes nothing else.
2560
+ *
2561
+ * @example
2562
+ * // Application owns the interface.
2563
+ * new SuperDoc({ selector: '#editor', document: file, ui: false });
2564
+ *
2565
+ * @example
2566
+ * // Built-in toolbar and search, no comments or context menu.
2567
+ * new SuperDoc({
2568
+ * selector: '#editor',
2569
+ * document: file,
2570
+ * ui: {
2571
+ * toolbar: { container: '#toolbar' },
2572
+ * search: true,
2573
+ * comments: false,
2574
+ * contextMenu: false,
2575
+ * },
2576
+ * });
2577
+ */
2578
+ ui?: false | UIConfig;
2579
+ /**
2580
+ * What the user is permitted to do. Independent of {@link Config.ui}: a
2581
+ * `readOnly` policy still applies when the application renders its own
2582
+ * comment UI.
2583
+ */
2584
+ interaction?: InteractionConfig;
2585
+ /**
2586
+ * Shared configuration for dialogs and floating overlays, including ones
2587
+ * opened through `superdoc.openSurface()`. Stays active under `ui: false`.
2588
+ */
2589
+ surfaces?: SurfacesConfig;
2383
2590
  /** Modules to load. */
2384
2591
  modules?: Modules;
2385
2592
  /** Top-level override for permission checks. */