canopycms 0.7.0 → 0.8.0

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 (147) hide show
  1. package/CHANGELOG.md +201 -47
  2. package/dist/api/admin-branch-health.js +47 -20
  3. package/dist/api/admin.d.ts +13 -1
  4. package/dist/api/admin.js +14 -24
  5. package/dist/api/branch-changes.d.ts +87 -0
  6. package/dist/api/branch-changes.js +269 -0
  7. package/dist/api/branch-entry-changes.d.ts +83 -0
  8. package/dist/api/branch-entry-changes.js +262 -0
  9. package/dist/api/branch-merge.js +2 -2
  10. package/dist/api/branch-review.js +35 -4
  11. package/dist/api/branch-status.js +14 -5
  12. package/dist/api/branch-withdraw.js +7 -0
  13. package/dist/api/branch.d.ts +13 -1
  14. package/dist/api/branch.js +171 -97
  15. package/dist/api/client.d.ts +15 -0
  16. package/dist/api/client.js +13 -1
  17. package/dist/api/content.d.ts +18 -18
  18. package/dist/api/content.js +1 -1
  19. package/dist/api/github-sync.js +7 -3
  20. package/dist/api/groups.d.ts +4 -4
  21. package/dist/api/pr-comment-sync.d.ts +9 -8
  22. package/dist/api/pr-comment-sync.js +23 -13
  23. package/dist/api/routes.js +4 -0
  24. package/dist/api/types.d.ts +3 -1
  25. package/dist/authorization/groups/schema.d.ts +4 -4
  26. package/dist/authorization/protected-branch.d.ts +3 -3
  27. package/dist/authorization/protected-branch.js +3 -3
  28. package/dist/branch-metadata-file.d.ts +84 -0
  29. package/dist/branch-metadata-file.js +9 -0
  30. package/dist/branch-metadata.d.ts +4 -4
  31. package/dist/branch-metadata.js +4 -5
  32. package/dist/branch-param.d.ts +24 -0
  33. package/dist/branch-param.js +87 -0
  34. package/dist/branch-provisioning.d.ts +6 -3
  35. package/dist/branch-provisioning.js +12 -10
  36. package/dist/branch-registry.d.ts +6 -0
  37. package/dist/branch-registry.js +12 -3
  38. package/dist/branch-workspace.js +22 -15
  39. package/dist/cli/cli.js +1060 -172
  40. package/dist/cli/generate-ai-content.js +732 -101
  41. package/dist/cli/init.js +126 -0
  42. package/dist/comment-store.d.ts +3 -0
  43. package/dist/comment-store.js +4 -1
  44. package/dist/config/schemas/config.d.ts +3 -0
  45. package/dist/config/schemas/config.js +2 -0
  46. package/dist/config/types.d.ts +11 -0
  47. package/dist/content-id-index.d.ts +5 -0
  48. package/dist/content-id-index.js +1 -1
  49. package/dist/content-listing.d.ts +53 -0
  50. package/dist/content-listing.js +123 -27
  51. package/dist/content-reader.js +23 -16
  52. package/dist/context.js +8 -7
  53. package/dist/editor/BranchManager.d.ts +7 -3
  54. package/dist/editor/BranchManager.js +51 -6
  55. package/dist/editor/Editor.js +166 -34
  56. package/dist/editor/EntryNavigator.d.ts +34 -10
  57. package/dist/editor/EntryNavigator.js +185 -29
  58. package/dist/editor/admin/SystemHealthPanel.js +36 -1
  59. package/dist/editor/admin/useSystemHealth.js +1 -0
  60. package/dist/editor/branch-names.d.ts +43 -0
  61. package/dist/editor/branch-names.js +75 -0
  62. package/dist/editor/components/BranchPill.d.ts +2 -2
  63. package/dist/editor/components/BranchPill.js +2 -2
  64. package/dist/editor/components/EditorHeader.d.ts +3 -1
  65. package/dist/editor/components/EditorHeader.js +2 -2
  66. package/dist/editor/components/EntryChangesDrawer.d.ts +51 -0
  67. package/dist/editor/components/EntryChangesDrawer.js +363 -0
  68. package/dist/editor/components/EntryChangesSkeleton.d.ts +9 -0
  69. package/dist/editor/components/EntryChangesSkeleton.js +7 -0
  70. package/dist/editor/components/EntryHeader.d.ts +9 -1
  71. package/dist/editor/components/EntryHeader.js +25 -3
  72. package/dist/editor/components/LazyEntryChangesDrawer.d.ts +16 -0
  73. package/dist/editor/components/LazyEntryChangesDrawer.js +45 -0
  74. package/dist/editor/components/ReviewDialogs.js +3 -3
  75. package/dist/editor/context/ApiClientContext.d.ts +9 -0
  76. package/dist/editor/context/ApiClientContext.js +26 -2
  77. package/dist/editor/context/index.d.ts +1 -1
  78. package/dist/editor/context/index.js +1 -1
  79. package/dist/editor/copy.d.ts +1 -0
  80. package/dist/editor/copy.js +1 -0
  81. package/dist/editor/editor-utils.d.ts +2 -0
  82. package/dist/editor/editor-utils.js +15 -0
  83. package/dist/editor/entry-diff.d.ts +143 -0
  84. package/dist/editor/entry-diff.js +593 -0
  85. package/dist/editor/hooks/create-branch-request.d.ts +11 -1
  86. package/dist/editor/hooks/create-branch-request.js +16 -6
  87. package/dist/editor/hooks/useBranchActions.d.ts +19 -2
  88. package/dist/editor/hooks/useBranchActions.js +29 -20
  89. package/dist/editor/hooks/useBranchChangesData.d.ts +57 -0
  90. package/dist/editor/hooks/useBranchChangesData.js +134 -0
  91. package/dist/editor/hooks/useBranchManager.d.ts +16 -0
  92. package/dist/editor/hooks/useBranchManager.js +142 -54
  93. package/dist/editor/hooks/useDraftManager.js +6 -2
  94. package/dist/editor/hooks/useEntryManager.d.ts +18 -2
  95. package/dist/editor/hooks/useEntryManager.js +36 -10
  96. package/dist/editor/utils/editor-errors.d.ts +5 -2
  97. package/dist/git-manager.d.ts +71 -0
  98. package/dist/git-manager.js +270 -1
  99. package/dist/http/handler.js +11 -9
  100. package/dist/operating-mode/branch-namespace-fixtures.d.ts +17 -0
  101. package/dist/operating-mode/branch-namespace-fixtures.js +65 -0
  102. package/dist/operating-mode/branch-namespace.d.ts +34 -0
  103. package/dist/operating-mode/branch-namespace.js +121 -0
  104. package/dist/operating-mode/client-unsafe-strategy.js +7 -0
  105. package/dist/operating-mode/types.d.ts +8 -0
  106. package/dist/paths/branch-name.d.ts +11 -19
  107. package/dist/paths/branch-name.js +17 -27
  108. package/dist/paths/index.d.ts +2 -2
  109. package/dist/paths/index.js +2 -2
  110. package/dist/paths/resolve.d.ts +0 -1
  111. package/dist/paths/resolve.js +0 -1
  112. package/dist/paths/validation.d.ts +40 -1
  113. package/dist/paths/validation.js +83 -4
  114. package/dist/pr-comments.d.ts +17 -9
  115. package/dist/pr-comments.js +53 -15
  116. package/dist/services.js +2 -1
  117. package/dist/submission-attribution.d.ts +22 -0
  118. package/dist/submission-attribution.js +62 -6
  119. package/dist/task-queue/worker-status.d.ts +6 -1
  120. package/dist/task-queue/worker-status.js +23 -2
  121. package/dist/types.d.ts +59 -0
  122. package/dist/version.d.ts +1 -1
  123. package/dist/version.js +1 -1
  124. package/dist/worker/branch-policy.d.ts +111 -16
  125. package/dist/worker/branch-policy.js +222 -17
  126. package/dist/worker/cms-worker.d.ts +24 -2
  127. package/dist/worker/cms-worker.js +35 -24
  128. package/dist/worker/git-sync.d.ts +3 -3
  129. package/dist/worker/git-sync.js +47 -1
  130. package/dist/worker/github-gateway-http.js +2 -2
  131. package/dist/worker/github-gateway-wire.d.ts +40 -25
  132. package/dist/worker/github-gateway-wire.js +14 -3
  133. package/dist/worker/github-gateway.d.ts +17 -6
  134. package/dist/worker/github-gateway.js +62 -6
  135. package/dist/worker/github-mirror.d.ts +7 -4
  136. package/dist/worker/github-mirror.js +27 -16
  137. package/dist/worker/history-rewrite.d.ts +23 -10
  138. package/dist/worker/history-rewrite.js +43 -20
  139. package/dist/worker/legacy-branch-fixtures.d.ts +21 -0
  140. package/dist/worker/legacy-branch-fixtures.js +71 -0
  141. package/dist/worker/rebase.d.ts +2 -1
  142. package/dist/worker/rebase.js +93 -36
  143. package/dist/worker/task-runner.d.ts +4 -1
  144. package/dist/worker/task-runner.js +77 -48
  145. package/dist/worker/unpublishable-branches.d.ts +34 -0
  146. package/dist/worker/unpublishable-branches.js +182 -0
  147. package/package.json +2 -1
@@ -9,8 +9,8 @@ const NOTE_ANNOUNCE_FROM = REQUEST_CHANGES_NOTE_MAX_LENGTH - 100;
9
9
  /** What the Approve dialog says; the pull request clause needs a pull request to be true. */
10
10
  export function approveMessage(branchName, hasPullRequest) {
11
11
  return hasPullRequest
12
- ? `Approve "${branchName}"? Its pull request stays open on GitHub for someone with access to merge it.`
13
- : `Approve "${branchName}"? It stays locked until it is withdrawn or changes are requested.`;
12
+ ? `Approve “${branchName}”? Its pull request stays open on GitHub for someone with access to merge it.`
13
+ : `Approve “${branchName}”? It stays locked until it is withdrawn or changes are requested.`;
14
14
  }
15
15
  /** The Request changes dialog's body: what happens, and an optional note for the editor. */
16
16
  export function RequestChangesForm({ branchName, hasPullRequest, initialNote = '', onNoteChange, }) {
@@ -18,7 +18,7 @@ export function RequestChangesForm({ branchName, hasPullRequest, initialNote = '
18
18
  // The first focus, which the dialog's focus trap gives, puts the caret after a prefilled note so
19
19
  // typing carries on from it; later focus keeps wherever the caret was left.
20
20
  const caretPlaced = useRef(false);
21
- return (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", children: `Send "${branchName}" back to its editor? It goes back to editing${hasPullRequest ? ', and its pull request becomes a draft' : ''}.` }), _jsxs(Stack, { gap: 4, children: [_jsx(Textarea, { label: "Note to the editor", description: "Saved as a comment on the branch.", value: note, onChange: (event) => {
21
+ return (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", children: `Send “${branchName}” back to its editor? It goes back to editing${hasPullRequest ? ', and its pull request becomes a draft' : ''}.` }), _jsxs(Stack, { gap: 4, children: [_jsx(Textarea, { label: "Note to the editor", description: "Saved as a comment on the branch.", value: note, onChange: (event) => {
22
22
  setNote(event.currentTarget.value);
23
23
  onNoteChange(event.currentTarget.value);
24
24
  }, maxLength: REQUEST_CHANGES_NOTE_MAX_LENGTH, autosize: true, minRows: 3, maxRows: 8, onFocus: (event) => {
@@ -7,6 +7,7 @@ import React from 'react';
7
7
  import { createApiClient } from '../../api/index.js';
8
8
  import type { OperatingMode } from '../../operating-mode/types.js';
9
9
  export type ApiClient = ReturnType<typeof createApiClient>;
10
+ type BranchSegments = ReadonlyMap<string, string>;
10
11
  export interface ApiClientProviderProps {
11
12
  children: React.ReactNode;
12
13
  /** Optional custom client for testing */
@@ -34,6 +35,13 @@ export declare function ApiClientProvider({ children, client, basePath, editorMo
34
35
  export declare function useOnUnauthorized(listener: () => void): void;
35
36
  /** As {@link useOnUnauthorized}, for responses refusing the editor's mode (`EDITOR_MODE_MISMATCH`). */
36
37
  export declare function useOnEditorModeMismatch(listener: () => void): void;
38
+ /**
39
+ * Tells the provider's own client the `/:branch` segment each listed branch's requests carry
40
+ * (name to segment); a name not in `segments` goes as `unlistedSegment` sends it. Set during
41
+ * render, not in an effect: a data hook fetches in a layout effect, before any effect here would
42
+ * run. Outside a provider it does nothing, and an injected `client` never reads the table.
43
+ */
44
+ export declare function useBranchSegments(segments: BranchSegments): void;
37
45
  /**
38
46
  * Hook to access the API client.
39
47
  * Must be used within an ApiClientProvider.
@@ -44,3 +52,4 @@ export declare function useApiClient(): ApiClient;
44
52
  * Useful for conditional usage or graceful degradation.
45
53
  */
46
54
  export declare function useOptionalApiClient(): ApiClient | null;
55
+ export {};
@@ -8,6 +8,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
8
8
  import { createContext, useContext, useEffect, useMemo, useState } from 'react';
9
9
  import { createApiClient } from '../../api/index.js';
10
10
  import { joinUrlPrefix } from '../../utils/url-prefix.js';
11
+ import { unlistedSegment } from '../branch-names.js';
11
12
  const ApiClientContext = createContext(null);
12
13
  function createClientSignal() {
13
14
  const listeners = new Set();
@@ -24,6 +25,17 @@ function createClientSignal() {
24
25
  },
25
26
  };
26
27
  }
28
+ /** The branch-name to `/:branch`-segment lookup the provider's client reads, replaced as listings arrive. */
29
+ function createSegmentTable() {
30
+ let segments = new Map();
31
+ return {
32
+ lookup: (branch) => segments.get(branch) ?? unlistedSegment(branch),
33
+ replace: (next) => {
34
+ segments = next;
35
+ },
36
+ };
37
+ }
38
+ const BranchSegmentsContext = createContext(null);
27
39
  const UnauthorizedSignalContext = createContext(null);
28
40
  const EditorModeMismatchSignalContext = createContext(null);
29
41
  /**
@@ -41,6 +53,7 @@ export function ApiClientProvider({ children, client, basePath, editorMode, }) {
41
53
  // Only a client built HERE reports to `useOnUnauthorized` and `useOnEditorModeMismatch`; an
42
54
  // injected `client` is used as-is, so its 401s and mode mismatches reach the auth gate only
43
55
  // through the gate's own whoami result.
56
+ const [segmentTable] = useState(createSegmentTable);
44
57
  const apiClient = useMemo(() => {
45
58
  return (client ??
46
59
  createApiClient({
@@ -48,9 +61,10 @@ export function ApiClientProvider({ children, client, basePath, editorMode, }) {
48
61
  onUnauthorized: unauthorized.emit,
49
62
  editorMode,
50
63
  onEditorModeMismatch: modeMismatch.emit,
64
+ branchSegment: segmentTable.lookup,
51
65
  }));
52
- }, [client, basePath, editorMode, unauthorized, modeMismatch]);
53
- return (_jsx(UnauthorizedSignalContext.Provider, { value: unauthorized, children: _jsx(EditorModeMismatchSignalContext.Provider, { value: modeMismatch, children: _jsx(ApiClientContext.Provider, { value: apiClient, children: children }) }) }));
66
+ }, [client, basePath, editorMode, unauthorized, modeMismatch, segmentTable]);
67
+ return (_jsx(UnauthorizedSignalContext.Provider, { value: unauthorized, children: _jsx(EditorModeMismatchSignalContext.Provider, { value: modeMismatch, children: _jsx(BranchSegmentsContext.Provider, { value: segmentTable.replace, children: _jsx(ApiClientContext.Provider, { value: apiClient, children: children }) }) }) }));
54
68
  }
55
69
  /**
56
70
  * Subscribe to every unauthenticated response (`ApiClientOptions.onUnauthorized`) the provider's
@@ -66,6 +80,16 @@ export function useOnEditorModeMismatch(listener) {
66
80
  const signal = useContext(EditorModeMismatchSignalContext);
67
81
  useEffect(() => signal?.subscribe(listener), [signal, listener]);
68
82
  }
83
+ /**
84
+ * Tells the provider's own client the `/:branch` segment each listed branch's requests carry
85
+ * (name to segment); a name not in `segments` goes as `unlistedSegment` sends it. Set during
86
+ * render, not in an effect: a data hook fetches in a layout effect, before any effect here would
87
+ * run. Outside a provider it does nothing, and an injected `client` never reads the table.
88
+ */
89
+ export function useBranchSegments(segments) {
90
+ const set = useContext(BranchSegmentsContext);
91
+ set?.(segments);
92
+ }
69
93
  /**
70
94
  * Hook to access the API client.
71
95
  * Must be used within an ApiClientProvider.
@@ -1,4 +1,4 @@
1
- export { ApiClientProvider, useApiClient, useOptionalApiClient, type ApiClient, type ApiClientProviderProps, } from './ApiClientContext.js';
1
+ export { ApiClientProvider, useApiClient, useBranchSegments, useOptionalApiClient, type ApiClient, type ApiClientProviderProps, } from './ApiClientContext.js';
2
2
  export { AssetContextProvider, authenticatedAssetBase, useAssetContext, type AssetContextValue, type AssetContextProviderProps, } from './AssetContext.js';
3
3
  export { SWRProvider, type SWRProviderProps } from './SWRProvider.js';
4
4
  export { useEditorLoading, useEditorModals, useEditorPreview } from './EditorStateContext.js';
@@ -1,4 +1,4 @@
1
- export { ApiClientProvider, useApiClient, useOptionalApiClient, } from './ApiClientContext.js';
1
+ export { ApiClientProvider, useApiClient, useBranchSegments, useOptionalApiClient, } from './ApiClientContext.js';
2
2
  export { AssetContextProvider, authenticatedAssetBase, useAssetContext, } from './AssetContext.js';
3
3
  export { SWRProvider } from './SWRProvider.js';
4
4
  export { useEditorLoading, useEditorModals, useEditorPreview } from './EditorStateContext.js';
@@ -20,6 +20,7 @@ export declare const EDITOR_ACTIONS: {
20
20
  readonly undoClear: "Undo clear";
21
21
  readonly changeUrl: "Change URL";
22
22
  readonly changeSlug: "Change slug";
23
+ readonly showChanges: "Show changes";
23
24
  };
24
25
  /**
25
26
  * Why a list at its `maxItems` adds nothing more, as a disabled Add's Tooltip or an input's hint.
@@ -20,6 +20,7 @@ export const EDITOR_ACTIONS = {
20
20
  undoClear: 'Undo clear',
21
21
  changeUrl: 'Change URL',
22
22
  changeSlug: 'Change slug',
23
+ showChanges: 'Show changes',
23
24
  };
24
25
  /**
25
26
  * Why a list at its `maxItems` adds nothing more, as a disabled Add's Tooltip or an input's hint.
@@ -17,6 +17,8 @@ type PreviewEntry = {
17
17
  itemType?: string;
18
18
  previewSrc?: string;
19
19
  };
20
+ /** `src` with its last `?branch=` value set to `branchName`; a `src` without one is returned as is. */
21
+ export declare const withPreviewBranch: (src: string, branchName: string) => string;
20
22
  /**
21
23
  * The entry's route on the host site, or `undefined` when it has no page. The first
22
24
  * `previewBaseByCollection` key present decides: for a root entry its own path
@@ -31,6 +31,21 @@ const appendBranch = (url, branchName) => {
31
31
  const separator = beforeHash.includes('?') ? '&' : '?';
32
32
  return `${beforeHash}${separator}branch=${encodeURIComponent(branchName)}${hash}`;
33
33
  };
34
+ /** `src` with its last `?branch=` value set to `branchName`; a `src` without one is returned as is. */
35
+ export const withPreviewBranch = (src, branchName) => {
36
+ const hashIndex = src.indexOf('#');
37
+ const beforeHash = hashIndex === -1 ? src : src.slice(0, hashIndex);
38
+ const queryIndex = beforeHash.indexOf('?');
39
+ if (queryIndex === -1)
40
+ return src;
41
+ const params = beforeHash.slice(queryIndex + 1).split('&');
42
+ const keys = params.map((param) => param.split('=', 1)[0]);
43
+ const at = keys.lastIndexOf('branch');
44
+ if (at === -1)
45
+ return src;
46
+ params[at] = `branch=${encodeURIComponent(branchName)}`;
47
+ return `${beforeHash.slice(0, queryIndex + 1)}${params.join('&')}${src.slice(beforeHash.length)}`;
48
+ };
34
49
  /**
35
50
  * The `previewBase` value under `key`, or `undefined` for none. An empty value counts as none, and
36
51
  * so does anything `Object.prototype` supplies.
@@ -0,0 +1,143 @@
1
+ /**
2
+ * What changed in one entry between two saved states, field by field, for the "Show changes"
3
+ * drawer. Pure and client-safe: it walks the entry type's schema, so each change carries the
4
+ * labels the form shows, and lists and blocks match their items by item key (`item-keys.ts`),
5
+ * so a reordered item reads as moved, not as one removed and one added.
6
+ */
7
+ import type { FieldConfig } from '../config/index.js';
8
+ import type { EntryChangeSide } from '../api/branch-entry-changes.js';
9
+ /** A run of text on one side of a text change; `added` and `removed` runs are the difference. */
10
+ export interface DiffPart {
11
+ value: string;
12
+ added?: true;
13
+ removed?: true;
14
+ }
15
+ /**
16
+ * One row of a line diff, its Before and After lines side by side: an unchanged line on both, a
17
+ * changed pair, or a line on one side only (the other side's cell is empty). `skipped` stands
18
+ * for unchanged lines left out between changes.
19
+ */
20
+ export type DiffRow = {
21
+ kind: 'same';
22
+ text: string;
23
+ } | {
24
+ kind: 'change';
25
+ before?: DiffPart[];
26
+ after?: DiffPart[];
27
+ } | {
28
+ kind: 'skipped';
29
+ count: number;
30
+ };
31
+ /**
32
+ * A text change: word by word for a short value, row by row with word marks for long text, or a
33
+ * line break added or removed at the very end, the only difference.
34
+ */
35
+ export type TextDiff = {
36
+ mode: 'words';
37
+ parts: DiffPart[];
38
+ } | {
39
+ mode: 'lines';
40
+ rows: DiffRow[];
41
+ } | {
42
+ mode: 'endLineBreak';
43
+ added: boolean;
44
+ };
45
+ /** A value as the drawer shows it. */
46
+ export type DisplayValue = {
47
+ kind: 'empty';
48
+ } | {
49
+ kind: 'text';
50
+ text: string;
51
+ long?: true;
52
+ } | {
53
+ kind: 'number';
54
+ value: number;
55
+ } | {
56
+ kind: 'boolean';
57
+ value: boolean;
58
+ }
59
+ /** `text`, its absolute local form; `dateOnly`: a calendar date, read as local midnight. */
60
+ | {
61
+ kind: 'date';
62
+ iso: string;
63
+ text: string;
64
+ dateOnly?: true;
65
+ } | {
66
+ kind: 'select';
67
+ label: string;
68
+ }
69
+ /** No `title` when the editor's entry list has no entry with this id. */
70
+ | {
71
+ kind: 'reference';
72
+ id: string;
73
+ title?: string;
74
+ } | {
75
+ kind: 'image';
76
+ src: string;
77
+ name: string;
78
+ alt?: string;
79
+ }
80
+ /** A list item's place, 1-based, among `of` items, for a move. */
81
+ | {
82
+ kind: 'position';
83
+ position: number;
84
+ of: number;
85
+ } | {
86
+ kind: 'list';
87
+ items: DisplayValue[];
88
+ }
89
+ /** A whole object, list item or block, as its labelled fields. */
90
+ | {
91
+ kind: 'fields';
92
+ fields: Array<{
93
+ label: string;
94
+ value: DisplayValue;
95
+ }>;
96
+ }
97
+ /** A value of a shape the schema doesn't describe, as JSON. */
98
+ | {
99
+ kind: 'raw';
100
+ text: string;
101
+ };
102
+ export interface EntryChange {
103
+ /** Stable among one diff's changes: its data path, list items named by key. */
104
+ key: string;
105
+ /** Human labels from the entry down to the change: "Page sections › Hero › Heading". */
106
+ path: string[];
107
+ kind: 'added' | 'removed' | 'changed' | 'moved';
108
+ /** A whole list item or block added, removed or moved, rather than a field's value. */
109
+ item?: true;
110
+ before?: DisplayValue;
111
+ after?: DisplayValue;
112
+ text?: TextDiff;
113
+ /** Both sides' images' alt texts, diffed by word. */
114
+ altText?: TextDiff;
115
+ /** The text differs too much to mark within the time allowed: show both sides whole. */
116
+ tooLargeToMark?: true;
117
+ }
118
+ export interface EntryDiff {
119
+ changes: EntryChange[];
120
+ /** Both sides exist and hold equal values: only the file's formatting differs. */
121
+ formattingOnly: boolean;
122
+ }
123
+ export interface EntryDiffInput {
124
+ /** The entry type's fields. */
125
+ fields: readonly FieldConfig[];
126
+ before: EntryChangeSide | null;
127
+ after: EntryChangeSide | null;
128
+ /** A referenced entry's title, from the entries the editor has listed. */
129
+ referenceTitle?: (id: string) => string | undefined;
130
+ }
131
+ /**
132
+ * The difference between two texts. Short single-line text is diffed by word; anything else by
133
+ * line, a removed line paired with the added line in its place diffed by word inside, and runs
134
+ * of unchanged lines cut to {@link CONTEXT_LINES} beside each change. Words are diffed with
135
+ * whitespace as tokens (`diffWordsWithSpace`): `diffWords` gives an unchanged run the new
136
+ * text's spacing, so one side read back from its parts would not be the text it was.
137
+ * Undefined once `deadline` (a `Date.now()`) passes: no line diff starts after it. A short word
138
+ * diff runs until the later `wordDeadline`.
139
+ * @internal Exported for tests.
140
+ */
141
+ export declare function textDiff(before: string, after: string, long?: boolean, deadline?: number, wordDeadline?: number): TextDiff | undefined;
142
+ /** The entry's changes between `before` and `after`, in schema order. */
143
+ export declare function computeEntryDiff({ fields, before, after, referenceTitle, }: EntryDiffInput): EntryDiff;