@playpilot/tpi 8.39.0 → 8.40.0-beta.2
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/dist/editorial.mount.js +8 -8
- package/dist/link-injections.js +2 -2
- package/dist/mount.css +1 -1
- package/dist/mount.js +6 -6
- package/package.json +1 -1
- package/src/lib/injection.ts +18 -5
- package/src/lib/types/config.d.ts +5 -0
- package/src/routes/components/Participants/Participant.svelte +7 -3
- package/src/tests/lib/injection.test.js +70 -1
package/package.json
CHANGED
package/src/lib/injection.ts
CHANGED
|
@@ -32,6 +32,7 @@ export function injectLinksInDocument(elements: HTMLElement[], injections: LinkI
|
|
|
32
32
|
const foundInjections = validInjections.filter(i => fullText.includes(cleanPhrase(i.sentence)))
|
|
33
33
|
|
|
34
34
|
const failedMessages: Record<string, string> = {}
|
|
35
|
+
const aiInjectionSidsCount: Record<string, number> = {}
|
|
35
36
|
|
|
36
37
|
let injectionIndex = -1 // This index is used in Option 3 below.
|
|
37
38
|
for (const injection of sortInjections(foundInjections)) {
|
|
@@ -40,9 +41,20 @@ export function injectLinksInDocument(elements: HTMLElement[], injections: LinkI
|
|
|
40
41
|
// TODO: REMOVE ME. This is a temporary hack. Popularity should be on participant_details but currently it is only on the injection.
|
|
41
42
|
if (injection.type === 'participant') injection.participant_details!.popularity ||= injection.popularity
|
|
42
43
|
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
if (!injection.manual) {
|
|
45
|
+
const previousCount = aiInjectionSidsCount[injection.sid] || 0
|
|
46
|
+
|
|
47
|
+
if (previousCount >= (window.PlayPilotLinkInjections?.config?.unique_injection_count_limit || 10)) {
|
|
48
|
+
failedMessages[injection.key] = 'Injection was withheld because it exceeded the maximum unique count.'
|
|
49
|
+
continue
|
|
50
|
+
} else {
|
|
51
|
+
aiInjectionSidsCount[injection.sid] = previousCount + 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (injection.type === 'participant' && !doesParticipantMeetThreshold(mergedInjections, injection.participant_details!)) {
|
|
55
|
+
failedMessages[injection.key] = 'Participant does not meet popularity threshold.'
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
const elementIndex = elements.findIndex(element => cleanPhrase(element.innerText).includes(cleanPhrase(injection.sentence)))
|
|
@@ -52,7 +64,6 @@ export function injectLinksInDocument(elements: HTMLElement[], injections: LinkI
|
|
|
52
64
|
|
|
53
65
|
const nodeContainingText = findTextNodeContaining(injection.title, element, ['A'])
|
|
54
66
|
|
|
55
|
-
// Ignore if the found injection has no node or if it is inside a link.
|
|
56
67
|
if (!nodeContainingText?.nodeValue || isNodeInLink(nodeContainingText)) {
|
|
57
68
|
// We check once more where the text was found, this time without ignoring links
|
|
58
69
|
// so we can determine if the failure was due to it being in a link
|
|
@@ -222,6 +233,7 @@ function createLinkInjectionElement(injection: LinkInjection): { injectionElemen
|
|
|
222
233
|
// so that it is not directly inside of the link.
|
|
223
234
|
const injectionElement = document.createElement('span')
|
|
224
235
|
injectionElement.dataset.playpilotInjectionKey = injection.key
|
|
236
|
+
injectionElement.dataset.playpilotInjectionType = injection.type
|
|
225
237
|
|
|
226
238
|
const openInExplore = !!window.PlayPilotLinkInjections?.config?.open_tpi_links_in_explore
|
|
227
239
|
|
|
@@ -491,10 +503,11 @@ export function doesParticipantMeetThreshold(injections: LinkInjection[], partic
|
|
|
491
503
|
if (participant.popularity === null || participant.popularity === undefined) return true
|
|
492
504
|
|
|
493
505
|
const useArticleAverage = window.PlayPilotLinkInjections?.config?.participant_injection_popularity_threshold_use_article_average ?? true
|
|
494
|
-
const defaultThreshold =
|
|
506
|
+
const defaultThreshold = 0
|
|
495
507
|
const configThreshold = window.PlayPilotLinkInjections?.config?.participant_injection_popularity_threshold
|
|
496
508
|
const threshold = configThreshold ?? defaultThreshold
|
|
497
509
|
|
|
510
|
+
if (threshold === 0) return true
|
|
498
511
|
if (!useArticleAverage) return participant.popularity >= threshold
|
|
499
512
|
|
|
500
513
|
const averagePopularity = injections
|
|
@@ -115,6 +115,11 @@ export type ConfigResponse = {
|
|
|
115
115
|
*/
|
|
116
116
|
participant_injection_popularity_threshold_use_article_average?: boolean
|
|
117
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Include a limit of how many times a single titles/participants may appear in one article.
|
|
120
|
+
*/
|
|
121
|
+
unique_injection_count_limit?: number
|
|
122
|
+
|
|
118
123
|
/**
|
|
119
124
|
* These options are all relevant for the Explore component, which can be inserted as a widget on any page or as a modal.
|
|
120
125
|
* `explore_navigation_selector` is used to select the navigation element that should be copied and inserted _after_.
|
|
@@ -48,13 +48,13 @@
|
|
|
48
48
|
}
|
|
49
49
|
</script>
|
|
50
50
|
|
|
51
|
-
<div class="header" class:small>
|
|
51
|
+
<div class="header" class:small class:no-image={!participant.image}>
|
|
52
52
|
{#if participant.image}
|
|
53
53
|
<ParticipantImage {participant} />
|
|
54
54
|
{/if}
|
|
55
55
|
|
|
56
56
|
<div>
|
|
57
|
-
<div class="heading"
|
|
57
|
+
<div class="heading" use:heading={2} id="heading">{name}</div>
|
|
58
58
|
|
|
59
59
|
{#if description}
|
|
60
60
|
<p class="description" class:expanded={descriptionExpanded}>
|
|
@@ -116,6 +116,10 @@
|
|
|
116
116
|
padding: margin(1) margin(1) 0;
|
|
117
117
|
align-items: center;
|
|
118
118
|
}
|
|
119
|
+
|
|
120
|
+
&.no-image {
|
|
121
|
+
display: block;
|
|
122
|
+
}
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
.heading {
|
|
@@ -130,7 +134,7 @@
|
|
|
130
134
|
font-size: theme(detail-title-small-font-size, margin(1.25));
|
|
131
135
|
}
|
|
132
136
|
|
|
133
|
-
|
|
137
|
+
.no-image & {
|
|
134
138
|
margin-top: margin(0.5);
|
|
135
139
|
}
|
|
136
140
|
|
|
@@ -1038,6 +1038,22 @@ describe('injection.ts', () => {
|
|
|
1038
1038
|
|
|
1039
1039
|
expect(document.querySelectorAll('[data-playpilot-injection-key]')).toHaveLength(2)
|
|
1040
1040
|
})
|
|
1041
|
+
|
|
1042
|
+
it('Should include injection type on element', () => {
|
|
1043
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
1044
|
+
|
|
1045
|
+
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
1046
|
+
const injections = [
|
|
1047
|
+
{ ...generateInjection('This is a sentence with an injection.', 'a sentence'), type: 'participant', participant_details: participants[0] },
|
|
1048
|
+
{ ...generateInjection('This is a sentence with an injection.', 'an injection'), type: 'title' },
|
|
1049
|
+
]
|
|
1050
|
+
|
|
1051
|
+
// @ts-ignore
|
|
1052
|
+
injectLinksInDocument(elements, { aiInjections: injections, manualInjections: [] })
|
|
1053
|
+
|
|
1054
|
+
expect(/** @type {HTMLElement} */ (document.querySelectorAll('[data-playpilot-injection-key]')[0]).dataset.playpilotInjectionType).toBe('participant')
|
|
1055
|
+
expect(/** @type {HTMLElement} */ (document.querySelectorAll('[data-playpilot-injection-key]')[1]).dataset.playpilotInjectionType).toBe('title')
|
|
1056
|
+
})
|
|
1041
1057
|
})
|
|
1042
1058
|
|
|
1043
1059
|
describe('config.open_tpi_links_in_explore', () => {
|
|
@@ -1076,6 +1092,46 @@ describe('injection.ts', () => {
|
|
|
1076
1092
|
expect(openModalForInjectedLink).not.toHaveBeenCalled()
|
|
1077
1093
|
})
|
|
1078
1094
|
})
|
|
1095
|
+
|
|
1096
|
+
describe('config.unique_injection_count_limit', () => {
|
|
1097
|
+
it('Should not insert another link if the count for links of the same sid is met', () => {
|
|
1098
|
+
window.PlayPilotLinkInjections.config = {
|
|
1099
|
+
unique_injection_count_limit: 2,
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
1103
|
+
|
|
1104
|
+
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
1105
|
+
const injections = [
|
|
1106
|
+
{ ...generateInjection('This is a sentence with an injection.', 'This is'), sid: 'a' },
|
|
1107
|
+
{ ...generateInjection('This is a sentence with an injection.', 'a sentence'), sid: 'a' },
|
|
1108
|
+
{ ...generateInjection('This is a sentence with an injection.', 'an injection'), sid: 'a' },
|
|
1109
|
+
]
|
|
1110
|
+
|
|
1111
|
+
injectLinksInDocument(elements, { aiInjections: injections, manualInjections: [] })
|
|
1112
|
+
|
|
1113
|
+
expect(document.querySelectorAll('[data-playpilot-injection-key]')).toHaveLength(2)
|
|
1114
|
+
})
|
|
1115
|
+
|
|
1116
|
+
it('Should disregard limit for manual injections', () => {
|
|
1117
|
+
window.PlayPilotLinkInjections.config = {
|
|
1118
|
+
unique_injection_count_limit: 2,
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
1122
|
+
|
|
1123
|
+
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
1124
|
+
const aiInjections = [
|
|
1125
|
+
{ ...generateInjection('This is a sentence with an injection.', 'This is'), sid: 'a' },
|
|
1126
|
+
{ ...generateInjection('This is a sentence with an injection.', 'a sentence'), sid: 'a' },
|
|
1127
|
+
]
|
|
1128
|
+
const manualInjections = [{ ...generateInjection('This is a sentence with an injection.', 'an injection'), sid: 'a' }]
|
|
1129
|
+
|
|
1130
|
+
injectLinksInDocument(elements, { aiInjections, manualInjections })
|
|
1131
|
+
|
|
1132
|
+
expect(document.querySelectorAll('[data-playpilot-injection-key]')).toHaveLength(3)
|
|
1133
|
+
})
|
|
1134
|
+
})
|
|
1079
1135
|
})
|
|
1080
1136
|
|
|
1081
1137
|
describe('clearLinkInjections', () => {
|
|
@@ -1368,7 +1424,7 @@ describe('injection.ts', () => {
|
|
|
1368
1424
|
expect(doesParticipantMeetThreshold(injections, falseyParticipant)).toBe(false)
|
|
1369
1425
|
})
|
|
1370
1426
|
|
|
1371
|
-
it('Should return true if threshold is
|
|
1427
|
+
it('Should return true if threshold is 0, regardless of popularity', () => {
|
|
1372
1428
|
window.PlayPilotLinkInjections.config = {
|
|
1373
1429
|
participant_injection_popularity_threshold: 0,
|
|
1374
1430
|
}
|
|
@@ -1384,5 +1440,18 @@ describe('injection.ts', () => {
|
|
|
1384
1440
|
expect(doesParticipantMeetThreshold(injections, truthyParticipant)).toBe(true)
|
|
1385
1441
|
expect(doesParticipantMeetThreshold(injections, truthyParticipant2)).toBe(true)
|
|
1386
1442
|
})
|
|
1443
|
+
|
|
1444
|
+
it('Should use threshold of 0 by default', () => {
|
|
1445
|
+
const truthyParticipant = { ...participants[0], popularity: 0 }
|
|
1446
|
+
const truthyParticipant2 = { ...participants[0], popularity: 1 }
|
|
1447
|
+
|
|
1448
|
+
/** @type {import('$lib/types/injection').LinkInjection[]} */
|
|
1449
|
+
const injections = [
|
|
1450
|
+
{ ...generateInjection('This is a sentence with an injection.', 'an injection'), type: 'participant', participant_details: truthyParticipant },
|
|
1451
|
+
]
|
|
1452
|
+
|
|
1453
|
+
expect(doesParticipantMeetThreshold(injections, truthyParticipant)).toBe(true)
|
|
1454
|
+
expect(doesParticipantMeetThreshold(injections, truthyParticipant2)).toBe(true)
|
|
1455
|
+
})
|
|
1387
1456
|
})
|
|
1388
1457
|
})
|