@atlaskit/editor-plugin-show-diff 16.1.0 → 16.2.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.
- package/CHANGELOG.md +33 -0
- package/afm-products/tsconfig.json +3 -0
- package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +67 -22
- package/dist/cjs/pm-plugins/calculateDiff/simplifyChangesWithAttribution.js +242 -26
- package/dist/cjs/pm-plugins/decorations/colorSchemes/factory.js +71 -37
- package/dist/cjs/pm-plugins/decorations/colorSchemes/schemes.js +5 -1
- package/dist/cjs/pm-plugins/decorations/colorSchemes/types.js +0 -2
- package/dist/cjs/pm-plugins/decorations/createBlockChangedDecoration.js +21 -14
- package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +83 -3
- package/dist/cjs/pm-plugins/decorations/extractContributorTags.js +123 -43
- package/dist/cjs/pm-plugins/decorations/revealStyles.js +5 -1
- package/dist/cjs/pm-plugins/decorations/utils/absorbFirstChildMarginReset.js +33 -0
- package/dist/cjs/pm-plugins/decorations/utils/createMarginAbsorber.js +43 -0
- package/dist/cjs/pm-plugins/decorations/utils/createNodeShapedMarginSpacer.js +76 -0
- package/dist/cjs/pm-plugins/decorations/utils/safeResolve.js +24 -0
- package/dist/cjs/pm-plugins/getScrollableDecorations.js +48 -19
- package/dist/cjs/pm-plugins/utils/baseNodeName.js +25 -0
- package/dist/cjs/pm-plugins/utils/taggableBlockNodes.js +37 -0
- package/dist/cjs/ui/ContributorTag/contributorTagController.js +4 -4
- package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +39 -2
- package/dist/es2019/pm-plugins/calculateDiff/simplifyChangesWithAttribution.js +196 -10
- package/dist/es2019/pm-plugins/decorations/colorSchemes/factory.js +51 -19
- package/dist/es2019/pm-plugins/decorations/colorSchemes/schemes.js +3 -0
- package/dist/es2019/pm-plugins/decorations/colorSchemes/types.js +0 -2
- package/dist/es2019/pm-plugins/decorations/createBlockChangedDecoration.js +23 -14
- package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +83 -3
- package/dist/es2019/pm-plugins/decorations/extractContributorTags.js +60 -7
- package/dist/es2019/pm-plugins/decorations/revealStyles.js +5 -1
- package/dist/es2019/pm-plugins/decorations/utils/absorbFirstChildMarginReset.js +29 -0
- package/dist/es2019/pm-plugins/decorations/utils/createMarginAbsorber.js +39 -0
- package/dist/es2019/pm-plugins/decorations/utils/createNodeShapedMarginSpacer.js +69 -0
- package/dist/es2019/pm-plugins/decorations/utils/safeResolve.js +18 -0
- package/dist/es2019/pm-plugins/getScrollableDecorations.js +44 -19
- package/dist/es2019/pm-plugins/utils/baseNodeName.js +18 -0
- package/dist/es2019/pm-plugins/utils/taggableBlockNodes.js +28 -0
- package/dist/es2019/ui/ContributorTag/contributorTagController.js +4 -4
- package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +67 -22
- package/dist/esm/pm-plugins/calculateDiff/simplifyChangesWithAttribution.js +243 -26
- package/dist/esm/pm-plugins/decorations/colorSchemes/factory.js +71 -37
- package/dist/esm/pm-plugins/decorations/colorSchemes/schemes.js +5 -1
- package/dist/esm/pm-plugins/decorations/colorSchemes/types.js +0 -2
- package/dist/esm/pm-plugins/decorations/createBlockChangedDecoration.js +21 -14
- package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +83 -3
- package/dist/esm/pm-plugins/decorations/extractContributorTags.js +123 -43
- package/dist/esm/pm-plugins/decorations/revealStyles.js +5 -1
- package/dist/esm/pm-plugins/decorations/utils/absorbFirstChildMarginReset.js +28 -0
- package/dist/esm/pm-plugins/decorations/utils/createMarginAbsorber.js +37 -0
- package/dist/esm/pm-plugins/decorations/utils/createNodeShapedMarginSpacer.js +70 -0
- package/dist/esm/pm-plugins/decorations/utils/safeResolve.js +18 -0
- package/dist/esm/pm-plugins/getScrollableDecorations.js +48 -19
- package/dist/esm/pm-plugins/utils/baseNodeName.js +20 -0
- package/dist/esm/pm-plugins/utils/taggableBlockNodes.js +32 -0
- package/dist/esm/ui/ContributorTag/contributorTagController.js +4 -4
- package/dist/types/pm-plugins/calculateDiff/simplifyChangesWithAttribution.d.ts +20 -1
- package/dist/types/pm-plugins/decorations/colorSchemes/factory.d.ts +4 -11
- package/dist/types/pm-plugins/decorations/colorSchemes/types.d.ts +9 -8
- package/dist/types/pm-plugins/decorations/utils/absorbFirstChildMarginReset.d.ts +23 -0
- package/dist/types/pm-plugins/decorations/utils/createMarginAbsorber.d.ts +23 -0
- package/dist/types/pm-plugins/decorations/utils/createNodeShapedMarginSpacer.d.ts +26 -0
- package/dist/types/pm-plugins/decorations/utils/safeResolve.d.ts +13 -0
- package/dist/types/pm-plugins/getScrollableDecorations.d.ts +2 -2
- package/dist/types/pm-plugins/utils/baseNodeName.d.ts +16 -0
- package/dist/types/pm-plugins/utils/taggableBlockNodes.d.ts +14 -0
- package/package.json +14 -5
|
@@ -172,9 +172,44 @@ const calculateNodesForBlockDecoration = ({
|
|
|
172
172
|
showIndicators = false,
|
|
173
173
|
diffType,
|
|
174
174
|
coarseTableCellsOnly = false,
|
|
175
|
-
tagMountContext
|
|
175
|
+
tagMountContext,
|
|
176
|
+
nodeRanges
|
|
176
177
|
}) => {
|
|
177
178
|
const decorations = [];
|
|
179
|
+
|
|
180
|
+
// Exact-nodes path: only reached for a split change (EDITOR-8932), which names the nodes it
|
|
181
|
+
// owns. Deliberately kept separate from the walk below rather than sharing it.
|
|
182
|
+
if (nodeRanges) {
|
|
183
|
+
for (const {
|
|
184
|
+
from: pos,
|
|
185
|
+
to: nodeEnd
|
|
186
|
+
} of nodeRanges) {
|
|
187
|
+
const node = doc.nodeAt(pos);
|
|
188
|
+
if (!(node !== null && node !== void 0 && node.isBlock)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const isActive = isRangeActive(activeIndexPos, pos, nodeEnd);
|
|
192
|
+
decorations.push(...createBlockChangedDecoration({
|
|
193
|
+
attributionKey,
|
|
194
|
+
change: {
|
|
195
|
+
from: pos,
|
|
196
|
+
to: nodeEnd,
|
|
197
|
+
name: node.type.name
|
|
198
|
+
},
|
|
199
|
+
colorScheme,
|
|
200
|
+
isInserted,
|
|
201
|
+
isActive,
|
|
202
|
+
shouldHideDeleted,
|
|
203
|
+
showContributorTags,
|
|
204
|
+
showIndicators,
|
|
205
|
+
doc,
|
|
206
|
+
diffType,
|
|
207
|
+
tagMountContext
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
return decorations;
|
|
211
|
+
}
|
|
212
|
+
|
|
178
213
|
// Coarse path: inside the large table, only cell/header overlays are kept — the
|
|
179
214
|
// table/row/paragraph decorations there are redundant and cause expensive per-cell
|
|
180
215
|
// re-render. Blocks outside the table keep their normal decorations.
|
|
@@ -420,7 +455,7 @@ const calculateDiffDecorationsInner = ({
|
|
|
420
455
|
tr,
|
|
421
456
|
attributedChanges
|
|
422
457
|
});
|
|
423
|
-
changes = collapseOverlappingChanges(uncollapsedChanges);
|
|
458
|
+
changes = collapseOverlappingChanges(uncollapsedChanges, tr.doc);
|
|
424
459
|
|
|
425
460
|
// Still colours-only: a tags-only diff keeps the plain colour scheme.
|
|
426
461
|
if (attributionColoringEnabled) {
|
|
@@ -570,6 +605,8 @@ const calculateDiffDecorationsInner = ({
|
|
|
570
605
|
doc: tr.doc,
|
|
571
606
|
from: change.fromB,
|
|
572
607
|
to: change.toB,
|
|
608
|
+
// A split piece's own range no longer covers the container it opened (EDITOR-8932).
|
|
609
|
+
nodeRanges: change.blockNodeRangesB,
|
|
573
610
|
colorScheme: changeColorScheme,
|
|
574
611
|
...(isExtendedEnabled(diffType) && {
|
|
575
612
|
isInserted,
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { simplifyChanges } from 'prosemirror-changeset';
|
|
2
2
|
import { getAttributionKey } from '../decorations/colorSchemes/attributions';
|
|
3
3
|
import { optimizeChanges } from './optimizeChanges';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A change, plus the block nodes it owns when it is one piece of a split — a piece's own range no
|
|
7
|
+
* longer covers the container it opened. Absent on an unsplit change, which decorates the blocks
|
|
8
|
+
* inside its own range as before.
|
|
9
|
+
*/
|
|
10
|
+
|
|
4
11
|
const getAttributionIdentity = change => {
|
|
5
12
|
const identities = new Set();
|
|
6
13
|
for (const span of [...change.deleted, ...change.inserted]) {
|
|
@@ -19,20 +26,178 @@ const getAttributionIdentity = change => {
|
|
|
19
26
|
};
|
|
20
27
|
const byNewDocRange = (left, right) => left.fromB - right.fromB || left.toB - right.toB;
|
|
21
28
|
|
|
29
|
+
/** Union of the given ranges, with overlapping and touching ones merged. */
|
|
30
|
+
const mergeRanges = ranges => [...ranges].sort((left, right) => left.from - right.from).reduce((merged, range) => {
|
|
31
|
+
const last = merged[merged.length - 1];
|
|
32
|
+
if (last && range.from <= last.to) {
|
|
33
|
+
last.to = Math.max(last.to, range.to);
|
|
34
|
+
return merged;
|
|
35
|
+
}
|
|
36
|
+
return [...merged, {
|
|
37
|
+
...range
|
|
38
|
+
}];
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
/** Whether `inner` sits wholly inside `outer` and covers less of the document. */
|
|
42
|
+
const isContained = (outer, inner) => inner.toB > inner.fromB && outer.fromB <= inner.fromB && inner.toB <= outer.toB && inner.toB - inner.fromB < outer.toB - outer.fromB;
|
|
43
|
+
|
|
22
44
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
45
|
+
* The block nodes in `change` whose opening position `ownsPosition` claims. Ownership is by opening
|
|
46
|
+
* position so a block another contributor added inside a container is not claimed by whoever opened
|
|
47
|
+
* the container. `undefined` for a range this document cannot resolve, which falls the piece back
|
|
48
|
+
* to decorating its own range.
|
|
49
|
+
*/
|
|
50
|
+
const blockNodeRanges = (doc, change, ownsPosition) => {
|
|
51
|
+
if (!doc || change.from >= change.to || change.to > doc.content.size) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const ranges = [];
|
|
55
|
+
doc.nodesBetween(change.from, change.to, (node, pos) => {
|
|
56
|
+
if (node.isBlock && ownsPosition(pos) && pos + node.nodeSize <= change.to) {
|
|
57
|
+
ranges.push({
|
|
58
|
+
from: pos,
|
|
59
|
+
to: pos + node.nodeSize
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
});
|
|
64
|
+
return ranges;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Splits a change whose spans name more than one contributor into one change per run of consecutive
|
|
69
|
+
* spans sharing a key.
|
|
26
70
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
71
|
+
* `prosemirror-changeset` merges contiguous inserted content into one change while keeping per-span
|
|
72
|
+
* data, so a panel inserted by one contributor and edited inside by another arrives as a single
|
|
73
|
+
* change holding every span — and the latest-writer rule credited all of it, panel included, to
|
|
74
|
+
* whoever wrote last (EDITOR-8932).
|
|
30
75
|
*/
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
return
|
|
76
|
+
const splitMixedIdentityChanges = (changes, doc) => changes.flatMap(change => {
|
|
77
|
+
if (getAttributionIdentity(change).mergeable) {
|
|
78
|
+
return [change];
|
|
79
|
+
}
|
|
80
|
+
const runs = [];
|
|
81
|
+
let offset = change.fromB;
|
|
82
|
+
for (const span of change.inserted) {
|
|
83
|
+
const key = getAttributionKey(span.data);
|
|
84
|
+
const last = runs[runs.length - 1];
|
|
85
|
+
if (last && last.key === key) {
|
|
86
|
+
last.to = offset + span.length;
|
|
87
|
+
last.spans.push(span);
|
|
88
|
+
} else {
|
|
89
|
+
runs.push({
|
|
90
|
+
key,
|
|
91
|
+
from: offset,
|
|
92
|
+
to: offset + span.length,
|
|
93
|
+
spans: [span]
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
offset += span.length;
|
|
34
97
|
}
|
|
35
98
|
|
|
99
|
+
// The offsets above are only positions if the spans partition the inserted range.
|
|
100
|
+
if (runs.length < 2 || offset !== change.toB) {
|
|
101
|
+
return [change];
|
|
102
|
+
}
|
|
103
|
+
return runs.map((run, index) => {
|
|
104
|
+
const owned = blockNodeRanges(doc, {
|
|
105
|
+
from: change.fromB,
|
|
106
|
+
to: change.toB
|
|
107
|
+
}, pos => pos >= run.from && pos < run.to);
|
|
108
|
+
return {
|
|
109
|
+
...change,
|
|
110
|
+
fromA: index === 0 ? change.fromA : change.toA,
|
|
111
|
+
fromB: run.from,
|
|
112
|
+
toB: run.to,
|
|
113
|
+
deleted: index === 0 ? change.deleted : [],
|
|
114
|
+
inserted: run.spans,
|
|
115
|
+
...(owned ? {
|
|
116
|
+
blockNodeRangesB: owned
|
|
117
|
+
} : {})
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Subtracts every other author's contained change from a change's range, so both contributions
|
|
124
|
+
* survive. Applies when the contributors arrive as separate changes rather than as spans of one —
|
|
125
|
+
* the `step` diff type. A contained change by the same author, and one that only partially
|
|
126
|
+
* overlaps, are left for the collapse below.
|
|
127
|
+
*
|
|
128
|
+
* The leading fragment keeps the deleted side, so one deletion is not drawn once per fragment.
|
|
129
|
+
*/
|
|
130
|
+
const splitContainedChanges = (changes, doc) => changes.flatMap(change => {
|
|
131
|
+
const {
|
|
132
|
+
key
|
|
133
|
+
} = getAttributionIdentity(change);
|
|
134
|
+
const inner = mergeRanges(changes.filter(other => {
|
|
135
|
+
const otherKey = getAttributionIdentity(other).key;
|
|
136
|
+
return other !== change && key !== undefined && otherKey !== undefined && otherKey !== key && isContained(change, other);
|
|
137
|
+
}).map(({
|
|
138
|
+
fromB,
|
|
139
|
+
toB
|
|
140
|
+
}) => ({
|
|
141
|
+
from: fromB,
|
|
142
|
+
to: toB
|
|
143
|
+
})));
|
|
144
|
+
if (inner.length === 0) {
|
|
145
|
+
return [change];
|
|
146
|
+
}
|
|
147
|
+
const gaps = [];
|
|
148
|
+
let cursor = change.fromB;
|
|
149
|
+
for (const {
|
|
150
|
+
from,
|
|
151
|
+
to
|
|
152
|
+
} of inner) {
|
|
153
|
+
if (from > cursor) {
|
|
154
|
+
gaps.push({
|
|
155
|
+
from: cursor,
|
|
156
|
+
to: from
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
cursor = Math.max(cursor, to);
|
|
160
|
+
}
|
|
161
|
+
if (change.toB > cursor) {
|
|
162
|
+
gaps.push({
|
|
163
|
+
from: cursor,
|
|
164
|
+
to: change.toB
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Subtracted end to end, so nothing is left to highlight — but the blocks are still owned.
|
|
169
|
+
const fragments = gaps.length > 0 ? gaps : [{
|
|
170
|
+
from: change.toB,
|
|
171
|
+
to: change.toB
|
|
172
|
+
}];
|
|
173
|
+
// The outer author's: every block in their range that no inner change opened.
|
|
174
|
+
const owned = blockNodeRanges(doc, {
|
|
175
|
+
from: change.fromB,
|
|
176
|
+
to: change.toB
|
|
177
|
+
}, pos => !inner.some(({
|
|
178
|
+
from,
|
|
179
|
+
to
|
|
180
|
+
}) => pos >= from && pos < to));
|
|
181
|
+
return fragments.map((fragment, index) => ({
|
|
182
|
+
...change,
|
|
183
|
+
fromA: index === 0 ? change.fromA : change.toA,
|
|
184
|
+
fromB: fragment.from,
|
|
185
|
+
toB: fragment.to,
|
|
186
|
+
deleted: index === 0 ? change.deleted : [],
|
|
187
|
+
...(owned ? {
|
|
188
|
+
blockNodeRangesB: index === 0 ? owned : []
|
|
189
|
+
} : {})
|
|
190
|
+
}));
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The blocks a merged change owns. Both sides must name theirs, or the merge falls back to its own
|
|
195
|
+
* range — narrowing it could drop a block altogether.
|
|
196
|
+
*/
|
|
197
|
+
const mergeBlockNodeRanges = (left, right) => left.blockNodeRangesB && right.blockNodeRangesB ? [...left.blockNodeRangesB, ...right.blockNodeRangesB] : undefined;
|
|
198
|
+
|
|
199
|
+
/** Unions the changes whose new-document ranges strictly overlap, leaving touching ones alone. */
|
|
200
|
+
const collapse = changes => {
|
|
36
201
|
// A zero-length new-document range paints no inline decoration, so it is kept aside rather than
|
|
37
202
|
// folded into a neighbouring insertion.
|
|
38
203
|
const emptyRanges = changes.filter(change => change.toB <= change.fromB);
|
|
@@ -40,13 +205,17 @@ export const collapseOverlappingChanges = changes => {
|
|
|
40
205
|
for (const change of changes.filter(change => change.toB > change.fromB).sort(byNewDocRange)) {
|
|
41
206
|
const current = collapsed[collapsed.length - 1];
|
|
42
207
|
if (current && change.fromB < current.toB) {
|
|
208
|
+
const blockNodeRangesB = mergeBlockNodeRanges(current, change);
|
|
43
209
|
collapsed[collapsed.length - 1] = {
|
|
44
210
|
fromA: Math.min(current.fromA, change.fromA),
|
|
45
211
|
toA: Math.max(current.toA, change.toA),
|
|
46
212
|
fromB: Math.min(current.fromB, change.fromB),
|
|
47
213
|
toB: Math.max(current.toB, change.toB),
|
|
48
214
|
deleted: [...current.deleted, ...change.deleted],
|
|
49
|
-
inserted: [...current.inserted, ...change.inserted]
|
|
215
|
+
inserted: [...current.inserted, ...change.inserted],
|
|
216
|
+
...(blockNodeRangesB ? {
|
|
217
|
+
blockNodeRangesB
|
|
218
|
+
} : {})
|
|
50
219
|
};
|
|
51
220
|
} else {
|
|
52
221
|
collapsed.push({
|
|
@@ -57,6 +226,23 @@ export const collapseOverlappingChanges = changes => {
|
|
|
57
226
|
return [...collapsed, ...emptyRanges].sort(byNewDocRange);
|
|
58
227
|
};
|
|
59
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Collapses changes whose new-document ranges strictly overlap. Attribution runs are simplified
|
|
231
|
+
* independently, so runs either side of an unattributed step can come back covering the same
|
|
232
|
+
* characters — which stacks two inline decorations and renders one contributor tag per copy.
|
|
233
|
+
*
|
|
234
|
+
* Merging keeps every span from both sides, so the latest-writer rule still decides whose tag is
|
|
235
|
+
* shown. Ranges that merely touch are left alone, so an unattributed edit next to an attributed one
|
|
236
|
+
* is not credited to it.
|
|
237
|
+
*
|
|
238
|
+
* Contributors are separated first, by `splitMixedIdentityChanges` and `splitContainedChanges`.
|
|
239
|
+
* Both leave ranges that only touch, so they survive the collapse.
|
|
240
|
+
*
|
|
241
|
+
* Carries no gate of its own: the only caller reaches it through the attributed changeset, which
|
|
242
|
+
* already requires `confluence_ncs_step_diffing_version_history`.
|
|
243
|
+
*/
|
|
244
|
+
export const collapseOverlappingChanges = (changes, doc) => collapse(splitContainedChanges(splitMixedIdentityChanges(changes, doc), doc));
|
|
245
|
+
|
|
60
246
|
/**
|
|
61
247
|
* Simplifies and optimizes consecutive changes without crossing an attribution boundary.
|
|
62
248
|
* A change which already contains more than one identity is deliberately kept isolated.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { agentBrandColorSchemes } from '@atlaskit/agent-color/agent-brand-color-schemes';
|
|
1
2
|
import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
3
|
import { getStandardDeletedTextDecorationStyle } from './getStandardDeletedTextDecorationStyle';
|
|
3
4
|
|
|
@@ -115,40 +116,46 @@ const textAccentMap = {
|
|
|
115
116
|
gray: "var(--ds-text-accent-gray, #505258)",
|
|
116
117
|
lime: "var(--ds-text-accent-lime, #4C6B1F)"
|
|
117
118
|
};
|
|
119
|
+
const getBrand = color => Object.prototype.hasOwnProperty.call(agentBrandColorSchemes, color) ? agentBrandColorSchemes[color] : undefined;
|
|
118
120
|
function bgSubtlest(color) {
|
|
119
|
-
|
|
121
|
+
var _getBrand$background, _getBrand;
|
|
122
|
+
return (_getBrand$background = (_getBrand = getBrand(color)) === null || _getBrand === void 0 ? void 0 : _getBrand.background) !== null && _getBrand$background !== void 0 ? _getBrand$background : bgSubtlestMap[color];
|
|
120
123
|
}
|
|
121
124
|
function bgSubtlestPressed(color) {
|
|
122
|
-
|
|
125
|
+
var _getBrand$background2, _getBrand2;
|
|
126
|
+
return (_getBrand$background2 = (_getBrand2 = getBrand(color)) === null || _getBrand2 === void 0 ? void 0 : _getBrand2.background) !== null && _getBrand$background2 !== void 0 ? _getBrand$background2 : bgSubtlestPressedMap[color];
|
|
123
127
|
}
|
|
124
128
|
function bgSubtler(color) {
|
|
125
|
-
|
|
129
|
+
var _getBrand$border, _getBrand3;
|
|
130
|
+
return (_getBrand$border = (_getBrand3 = getBrand(color)) === null || _getBrand3 === void 0 ? void 0 : _getBrand3.border) !== null && _getBrand$border !== void 0 ? _getBrand$border : bgSubtlerMap[color];
|
|
126
131
|
}
|
|
127
132
|
function bgBolder(color) {
|
|
128
|
-
|
|
133
|
+
var _getBrand$bold, _getBrand4;
|
|
134
|
+
return (_getBrand$bold = (_getBrand4 = getBrand(color)) === null || _getBrand4 === void 0 ? void 0 : _getBrand4.bold) !== null && _getBrand$bold !== void 0 ? _getBrand$bold : bgBolderMap[color];
|
|
129
135
|
}
|
|
130
136
|
function bgSubtlerPressed(color) {
|
|
131
|
-
|
|
137
|
+
var _getBrand$background3, _getBrand5;
|
|
138
|
+
return (_getBrand$background3 = (_getBrand5 = getBrand(color)) === null || _getBrand5 === void 0 ? void 0 : _getBrand5.background) !== null && _getBrand$background3 !== void 0 ? _getBrand$background3 : bgSubtlerPressedMap[color];
|
|
132
139
|
}
|
|
133
140
|
function borderAccent(color) {
|
|
134
|
-
|
|
141
|
+
var _getBrand$border2, _getBrand6;
|
|
142
|
+
return (_getBrand$border2 = (_getBrand6 = getBrand(color)) === null || _getBrand6 === void 0 ? void 0 : _getBrand6.border) !== null && _getBrand$border2 !== void 0 ? _getBrand$border2 : borderAccentMap[color];
|
|
135
143
|
}
|
|
136
144
|
function textAccent(color) {
|
|
137
|
-
|
|
145
|
+
var _getBrand$accentText, _getBrand7;
|
|
146
|
+
return (_getBrand$accentText = (_getBrand7 = getBrand(color)) === null || _getBrand7 === void 0 ? void 0 : _getBrand7.accentText) !== null && _getBrand$accentText !== void 0 ? _getBrand$accentText : textAccentMap[color];
|
|
147
|
+
}
|
|
148
|
+
function deletedBackground(color) {
|
|
149
|
+
var _getBrand$emphasisBac, _getBrand8;
|
|
150
|
+
return (_getBrand$emphasisBac = (_getBrand8 = getBrand(color)) === null || _getBrand8 === void 0 ? void 0 : _getBrand8.emphasisBackground) !== null && _getBrand$emphasisBac !== void 0 ? _getBrand$emphasisBac : bgSubtlest(color);
|
|
138
151
|
}
|
|
139
152
|
|
|
140
|
-
/**
|
|
141
|
-
* Presentation tokens for a contributor tag in one diff colour.
|
|
142
|
-
*
|
|
143
|
-
* A `bolder` fill rather than the `subtlest` tint the highlight uses: the tag is a label on the
|
|
144
|
-
* change, and at tag size a tint reads as a second, weaker highlight sitting above the real one.
|
|
145
|
-
* `inverse` is the only text tone that reads on a bolder fill, and it needs no per-colour map —
|
|
146
|
-
* which is also why the tag carries no border: the fill alone bounds it.
|
|
147
|
-
*/
|
|
153
|
+
/** Contributor tags use a bold fill with a contrasting foreground, including branded schemes. */
|
|
148
154
|
export function getAccentTokens(color) {
|
|
155
|
+
var _getBrand$boldText, _getBrand9;
|
|
149
156
|
return {
|
|
150
157
|
background: bgBolder(color),
|
|
151
|
-
text: "var(--ds-text-inverse, #FFFFFF)"
|
|
158
|
+
text: (_getBrand$boldText = (_getBrand9 = getBrand(color)) === null || _getBrand9 === void 0 ? void 0 : _getBrand9.boldText) !== null && _getBrand$boldText !== void 0 ? _getBrand$boldText : "var(--ds-text-inverse, #FFFFFF)"
|
|
152
159
|
};
|
|
153
160
|
}
|
|
154
161
|
|
|
@@ -170,13 +177,18 @@ function deletedCellOutline(colors) {
|
|
|
170
177
|
// --- Inserted content styles ---
|
|
171
178
|
|
|
172
179
|
function insertedInlineBorderColor(colors) {
|
|
173
|
-
|
|
180
|
+
var _getBrand$border3, _getBrand0;
|
|
181
|
+
return colors.insertedInlineBorderTone === 'backgroundHovered' ? (_getBrand$border3 = (_getBrand0 = getBrand(colors.insertColor)) === null || _getBrand0 === void 0 ? void 0 : _getBrand0.border) !== null && _getBrand$border3 !== void 0 ? _getBrand$border3 : bgSubtlestHoveredMap[colors.insertColor] : borderAccent(colors.insertColor);
|
|
174
182
|
}
|
|
175
183
|
|
|
176
184
|
/** Inline inserted content — default state. */
|
|
177
185
|
export function buildInsertStyle(colors) {
|
|
186
|
+
const brand = getBrand(colors.insertColor);
|
|
178
187
|
return convertToInlineCss({
|
|
179
188
|
background: bgSubtlest(colors.insertColor),
|
|
189
|
+
...(brand ? {
|
|
190
|
+
color: brand.text
|
|
191
|
+
} : {}),
|
|
180
192
|
textDecoration: 'underline',
|
|
181
193
|
textDecorationStyle: colors.insertUnderlineStyle,
|
|
182
194
|
textDecorationThickness: "var(--ds-space-025, 2px)",
|
|
@@ -196,8 +208,12 @@ export function buildInsertStyleInBlock(colors) {
|
|
|
196
208
|
|
|
197
209
|
/** Extended insert style — background + border-bottom underline. */
|
|
198
210
|
export function buildInsertStyleExtended(colors) {
|
|
211
|
+
const brand = getBrand(colors.insertColor);
|
|
199
212
|
return convertToInlineCss({
|
|
200
213
|
background: bgSubtlest(colors.insertColor),
|
|
214
|
+
...(brand ? {
|
|
215
|
+
color: brand.text
|
|
216
|
+
} : {}),
|
|
201
217
|
borderBottom: `2px solid ${insertedInlineBorderColor(colors)}`,
|
|
202
218
|
padding: `1px 0 2px`
|
|
203
219
|
});
|
|
@@ -213,8 +229,12 @@ export function buildInsertStyleInBlockExtended(colors) {
|
|
|
213
229
|
|
|
214
230
|
/** Active state of the extended insert style. */
|
|
215
231
|
export function buildInsertStyleExtendedActive(colors) {
|
|
232
|
+
const brand = getBrand(colors.insertColor);
|
|
216
233
|
return convertToInlineCss({
|
|
217
234
|
background: bgSubtlerPressed(colors.insertActiveColor),
|
|
235
|
+
...(brand ? {
|
|
236
|
+
color: brand.text
|
|
237
|
+
} : {}),
|
|
218
238
|
borderBottom: `2px solid ${borderAccent(colors.insertColor)}`,
|
|
219
239
|
padding: `1px 0 2px`
|
|
220
240
|
});
|
|
@@ -222,16 +242,24 @@ export function buildInsertStyleExtendedActive(colors) {
|
|
|
222
242
|
|
|
223
243
|
/** Extended insert style without underline — background + padding only. */
|
|
224
244
|
export function buildInsertStyleExtendedNoUnderline(colors) {
|
|
245
|
+
const brand = getBrand(colors.insertColor);
|
|
225
246
|
return convertToInlineCss({
|
|
226
247
|
background: bgSubtlest(colors.insertColor),
|
|
248
|
+
...(brand ? {
|
|
249
|
+
color: brand.text
|
|
250
|
+
} : {}),
|
|
227
251
|
padding: `1px 0 2px`
|
|
228
252
|
});
|
|
229
253
|
}
|
|
230
254
|
|
|
231
255
|
/** Active state of the no-underline extended insert style. */
|
|
232
256
|
export function buildInsertStyleExtendedNoUnderlineActive(colors) {
|
|
257
|
+
const brand = getBrand(colors.insertColor);
|
|
233
258
|
return convertToInlineCss({
|
|
234
259
|
background: bgSubtlerPressed(colors.insertActiveColor),
|
|
260
|
+
...(brand ? {
|
|
261
|
+
color: brand.text
|
|
262
|
+
} : {}),
|
|
235
263
|
padding: `1px 0 2px`
|
|
236
264
|
});
|
|
237
265
|
}
|
|
@@ -245,8 +273,12 @@ export function buildInsertStyleInBlockExtendedNoUnderline(_colors) {
|
|
|
245
273
|
|
|
246
274
|
/** Inline inserted content — active/focused state. */
|
|
247
275
|
export function buildInsertStyleActive(colors) {
|
|
276
|
+
const brand = getBrand(colors.insertColor);
|
|
248
277
|
return convertToInlineCss({
|
|
249
278
|
background: bgSubtlerPressed(colors.insertActiveColor),
|
|
279
|
+
...(brand ? {
|
|
280
|
+
color: brand.text
|
|
281
|
+
} : {}),
|
|
250
282
|
textDecoration: 'underline',
|
|
251
283
|
textDecorationStyle: colors.insertUnderlineStyle,
|
|
252
284
|
textDecorationThickness: "var(--ds-space-025, 2px)",
|
|
@@ -610,7 +642,7 @@ export function buildDeletedInlineContentStyleExtended(colors, isActive = false)
|
|
|
610
642
|
// Under 'underline' emphasis the highlight stays on its resting tint — the same one hover paints —
|
|
611
643
|
// so only the border carries the emphasis.
|
|
612
644
|
const isUnderlineEmphasis = isActive && usesUnderlineEmphasis(colors);
|
|
613
|
-
const backgroundColor = isActive && !isUnderlineEmphasis && colors.deletedInlineBorderTone === 'background' ? bgSubtlerPressed(colors.deleteActiveColor) :
|
|
645
|
+
const backgroundColor = isActive && !isUnderlineEmphasis && colors.deletedInlineBorderTone === 'background' ? bgSubtlerPressed(colors.deleteActiveColor) : deletedBackground(colors.deleteColor);
|
|
614
646
|
const borderColor = isUnderlineEmphasis ? borderAccent(colors.deleteActiveColor) : colors.deletedInlineBorderTone === 'background' ? backgroundColor : borderAccent(colors.deleteColor);
|
|
615
647
|
return convertToInlineCss({
|
|
616
648
|
backgroundColor,
|
|
@@ -822,7 +854,7 @@ export function getDeletedInlineRevealColors(colors) {
|
|
|
822
854
|
* paints at rest, so hovering restores the appearance rather than introducing a third one.
|
|
823
855
|
*/
|
|
824
856
|
export function getDeletedHighlightHoverColor(colors) {
|
|
825
|
-
return
|
|
857
|
+
return deletedBackground(colors.deleteColor);
|
|
826
858
|
}
|
|
827
859
|
|
|
828
860
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { agentBrandColorSchemes } from '@atlaskit/agent-color/agent-brand-color-schemes';
|
|
1
2
|
import { PARTICIPANT_COLOR_SCHEMES } from './types';
|
|
2
3
|
|
|
3
4
|
/** Green insertions, red deletions. */
|
|
@@ -74,10 +75,12 @@ const createAttributionScheme = color => {
|
|
|
74
75
|
};
|
|
75
76
|
};
|
|
76
77
|
const attributionColorSchemes = Object.fromEntries(PARTICIPANT_COLOR_SCHEMES.map(color => [color, createAttributionScheme(color)]));
|
|
78
|
+
const brandSchemes = Object.fromEntries(Object.keys(agentBrandColorSchemes).map(scheme => [scheme, createAttributionScheme(scheme)]));
|
|
77
79
|
|
|
78
80
|
/** Maps public schemes and attribution-only participant slots to their data objects. */
|
|
79
81
|
export const colorSchemeRegistry = {
|
|
80
82
|
...attributionColorSchemes,
|
|
83
|
+
...brandSchemes,
|
|
81
84
|
traditional: traditionalScheme,
|
|
82
85
|
standard: standardScheme
|
|
83
86
|
};
|
|
@@ -5,8 +5,6 @@ export const PARTICIPANT_COLOR_SCHEMES = ['red', 'blue', 'green', 'yellow', 'pur
|
|
|
5
5
|
|
|
6
6
|
/** ADS accent hues used by the public and participant colour schemes. */
|
|
7
7
|
|
|
8
|
-
/** A public scheme or an attribution participant slot resolved internally for one change. */
|
|
9
|
-
|
|
10
8
|
/**
|
|
11
9
|
* A show-diff colour scheme. Colour fields are ADS hues that `factory.ts` expands into token paths;
|
|
12
10
|
* the rest encode structural differences no colour can express. Ring widths, padding, radius and
|
|
@@ -4,6 +4,7 @@ import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
|
4
4
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
5
5
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
6
6
|
import { isExtendedEnabled } from '../isExtendedEnabled';
|
|
7
|
+
import { isTaggableBlockNode } from '../utils/taggableBlockNodes';
|
|
7
8
|
import { buildAddedCellOverlayRoundedStyle, buildAddedCellOverlayStyle, buildDeletedBlockNodeStyle, buildDeletedCellOverlayRoundedStyle, buildDeletedCellOverlayStyle, buildInsertedBlockNodeStyle } from './colorSchemes/factory';
|
|
8
9
|
import { colorSchemeRegistry, getLegacyColorScheme } from './colorSchemes/schemes';
|
|
9
10
|
import { createBlockIndicatorAnchorWidgets } from './createAnchorDecorationWidgets';
|
|
@@ -23,23 +24,28 @@ const UNSTYLED_NODES = ['mediaSingle', 'mediaGroup', 'table',
|
|
|
23
24
|
'heading', 'hardBreak', 'decisionList', 'taskList', 'bulletList', 'orderedList', 'layoutSection'];
|
|
24
25
|
const CELL_NODES = ['tableCell', 'tableHeader'];
|
|
25
26
|
|
|
26
|
-
/**
|
|
27
|
-
* Block nodes that host a contributor tag of their own — those whose whole box reads as one change.
|
|
28
|
-
* Lists and tables are excluded: they decorate per item and per cell, with no one-tag-per-block
|
|
29
|
-
* design yet. `blockCard` covers the datasource variant, which is the same node type.
|
|
30
|
-
*/
|
|
31
|
-
const TAGGABLE_BLOCK_NODES = new Set(['blockCard', 'blockquote', 'bodiedExtension', 'codeBlock', 'embedCard', 'expand', 'extension', 'media', 'multiBodiedExtension', 'panel', 'rule']);
|
|
32
|
-
|
|
33
27
|
/** Panels, rules and media can also sit in a table cell, which is out of scope for block tags. */
|
|
34
28
|
const isInsideTable = (doc, pos) => findParentNodeClosestToPos(doc.resolve(pos), node => node.type.name === 'table') !== undefined;
|
|
35
29
|
|
|
36
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Whether this block can host a contributor tag of its own.
|
|
32
|
+
*
|
|
33
|
+
* The node type is resolved from the document rather than matched on `change.name`, so a schema
|
|
34
|
+
* variant is recognised as the type it varies — see `isTaggableBlockNode`. `from` is the node's own
|
|
35
|
+
* position, so `nodeAt` returns exactly the node being decorated.
|
|
36
|
+
*/
|
|
37
37
|
const canTagBlock = ({
|
|
38
38
|
diffType,
|
|
39
39
|
doc,
|
|
40
|
-
from
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
from
|
|
41
|
+
}) => {
|
|
42
|
+
var _doc$nodeAt;
|
|
43
|
+
if (doc === undefined || !isExtendedEnabled(diffType)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const nodeType = (_doc$nodeAt = doc.nodeAt(from)) === null || _doc$nodeAt === void 0 ? void 0 : _doc$nodeAt.type;
|
|
47
|
+
return nodeType !== undefined && isTaggableBlockNode(nodeType) && !isInsideTable(doc, from);
|
|
48
|
+
};
|
|
43
49
|
|
|
44
50
|
/** Positioning context for the cell overlay widget decorations. */
|
|
45
51
|
const cellPositionStyle = convertToInlineCss({
|
|
@@ -176,8 +182,7 @@ export const createBlockChangedDecoration = ({
|
|
|
176
182
|
const shouldTagBlock = showContributorTags && canTagBlock({
|
|
177
183
|
diffType,
|
|
178
184
|
doc,
|
|
179
|
-
from: change.from
|
|
180
|
-
name: change.name
|
|
185
|
+
from: change.from
|
|
181
186
|
});
|
|
182
187
|
// Named so this block's own tag can position against it; see `createContributorTagWidget`.
|
|
183
188
|
const tagAnchorName = shouldTagBlock && isContributorTagWidgetEnabled() ? buildAnchorDecorationKey({
|
|
@@ -221,7 +226,9 @@ export const createBlockChangedDecoration = ({
|
|
|
221
226
|
key: 'cell-overlay-decoration'
|
|
222
227
|
}));
|
|
223
228
|
}
|
|
224
|
-
// isInserted is only read under the extended experience, so pass it unconditionally.
|
|
229
|
+
// isInserted is only read under the extended experience, so pass it unconditionally. `change.name`
|
|
230
|
+
// verbatim: base-name resolution stays in `canTagBlock`, which needs tags on, so a schema
|
|
231
|
+
// variant's legacy style is untouched — see `resolveBaseNodeName`.
|
|
225
232
|
const nodeStyle = getBlockNodeStyle({
|
|
226
233
|
nodeName: change.name,
|
|
227
234
|
colorScheme,
|
|
@@ -254,6 +261,8 @@ export const createBlockChangedDecoration = ({
|
|
|
254
261
|
})));
|
|
255
262
|
}
|
|
256
263
|
if (decorations.length === 0) {
|
|
264
|
+
// An unstyled node type (paragraph, heading, list, layout, …) emits no decoration at all, so
|
|
265
|
+
// there is nothing for a tag to hang off even when the node type would otherwise be taggable.
|
|
257
266
|
return decorations;
|
|
258
267
|
}
|
|
259
268
|
if (showIndicators && doc && isExtendedEnabled(diffType)) {
|