@picked/svelte 0.0.1-dev.1793776638554 → 0.0.1-dev.1793835766402
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/.tsbuildinfo +1 -1
- package/dist/plugins/devtools/DevToolsExperience.svelte +145 -93
- package/dist/plugins/devtools/customTools.d.ts +2 -2
- package/dist/plugins/devtools/derive/guide-sections/guide-sections.js +1 -1
- package/dist/plugins/devtools/derive/index.d.ts +3 -3
- package/dist/plugins/devtools/derive/index.js +1 -1
- package/dist/plugins/devtools/derive/personas/index.d.ts +2 -2
- package/dist/plugins/devtools/derive/personas/index.js +1 -1
- package/dist/plugins/devtools/derive/personas/personas.d.ts +88 -7
- package/dist/plugins/devtools/derive/personas/personas.js +1 -1
- package/dist/plugins/devtools/derive/plain-language/index.d.ts +1 -1
- package/dist/plugins/devtools/derive/plain-language/index.js +1 -1
- package/dist/plugins/devtools/derive/plain-language/plain-language.d.ts +3 -5
- package/dist/plugins/devtools/derive/plain-language/plain-language.js +1 -1
- package/dist/plugins/devtools/index.d.ts +1 -1
- package/dist/plugins/devtools/launcher/Launcher.svelte +13 -4
- package/dist/plugins/devtools/options.d.ts +6 -4
- package/dist/plugins/devtools/options.js +1 -1
- package/dist/plugins/devtools/review/journey/ExperienceDetails.svelte +129 -0
- package/dist/plugins/devtools/review/journey/JourneyPanel.svelte +54 -8
- package/dist/plugins/devtools/review/personas/PersonaBuilder.svelte +3 -34
- package/dist/plugins/devtools/review/personas/PersonaCard.svelte +226 -43
- package/dist/plugins/devtools/review/personas/PersonasPanel.svelte +171 -74
- package/dist/plugins/devtools/review/personas/load-persona.d.ts +41 -20
- package/dist/plugins/devtools/review/personas/load-persona.js +1 -1
- package/dist/plugins/devtools/review/personas/persona-route.d.ts +87 -0
- package/dist/plugins/devtools/review/personas/persona-route.js +1 -0
- package/dist/plugins/devtools/review/personas/persona-session.d.ts +94 -0
- package/dist/plugins/devtools/review/personas/persona-session.js +1 -0
- package/dist/plugins/devtools/sidebar/Sidebar.svelte +3 -68
- package/dist/plugins/devtools/sidebar/SidebarRow.svelte +5 -42
- package/dist/plugins/devtools/sidebar/nav.d.ts +18 -44
- package/dist/plugins/devtools/sidebar/nav.js +1 -1
- package/package.json +2 -2
- package/dist/plugins/devtools/screens/experience-instances/ExperienceInstancesListScreen.svelte +0 -10
- package/dist/plugins/devtools/screens/experiences/ExperienceDetailsScreen.svelte +0 -44
- package/dist/plugins/devtools/screens/experiences/ExperienceExperienceInstancesListScreen.svelte +0 -13
- package/dist/plugins/devtools/screens/experiences/ExperienceScreen.svelte +0 -32
- package/dist/plugins/devtools/screens/experiences/ExperienceTreeScreen.svelte +0 -500
- package/dist/plugins/devtools/screens/experiences/ExperiencesListScreen.svelte +0 -22
- package/dist/plugins/devtools/screens/features/FeaturesListScreen.svelte +0 -239
- package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +0 -286
- package/dist/plugins/devtools/screens/responses/ResponsesListScreen.svelte +0 -12
- package/dist/plugins/devtools/screens/responses/ResponsesTable.svelte +0 -88
|
@@ -18,16 +18,12 @@
|
|
|
18
18
|
import Sidebar from './sidebar/Sidebar.svelte'
|
|
19
19
|
import SidebarFooter from './sidebar/SidebarFooter.svelte'
|
|
20
20
|
import {
|
|
21
|
-
CUSTOM_TOOLS_OPEN_STORAGE_KEY,
|
|
22
21
|
CUSTOM_TOOLS_TAB,
|
|
23
|
-
DEVELOPER_OPEN_STORAGE_KEY,
|
|
24
22
|
TAB_STORAGE_KEY,
|
|
25
23
|
buildNav,
|
|
26
|
-
developerTabId,
|
|
27
|
-
isInCustomTools,
|
|
28
|
-
isInDeveloper,
|
|
29
24
|
readStored,
|
|
30
25
|
resolveTab,
|
|
26
|
+
screenTabId,
|
|
31
27
|
toolTabId,
|
|
32
28
|
writeStored,
|
|
33
29
|
} from './sidebar/nav'
|
|
@@ -46,6 +42,7 @@
|
|
|
46
42
|
TAB_INTROS,
|
|
47
43
|
configSourceLabel,
|
|
48
44
|
derivePosture,
|
|
45
|
+
applyEdits,
|
|
49
46
|
guideFixes,
|
|
50
47
|
guideSections,
|
|
51
48
|
readSavedPersonas,
|
|
@@ -62,26 +59,31 @@
|
|
|
62
59
|
VariationsPanel,
|
|
63
60
|
} from './review'
|
|
64
61
|
import {
|
|
62
|
+
openPersonaJourney,
|
|
65
63
|
screenQuestions,
|
|
66
64
|
takePendingLoad,
|
|
67
65
|
walkNotice,
|
|
68
66
|
walkPersona,
|
|
67
|
+
type PendingLoad,
|
|
69
68
|
type WalkSettings,
|
|
70
69
|
} from './review/personas/load-persona'
|
|
70
|
+
import {
|
|
71
|
+
prefillPersona,
|
|
72
|
+
readActivePersona,
|
|
73
|
+
takePersonaLink,
|
|
74
|
+
writeActivePersona,
|
|
75
|
+
type Prefill,
|
|
76
|
+
} from './review/personas/persona-session'
|
|
71
77
|
import ProjectScreen from './screens/project/ProjectScreen.svelte'
|
|
72
78
|
import ComponentsListScreen from './screens/components/ComponentsListScreen.svelte'
|
|
73
79
|
import ConfigScreen from './screens/config/ConfigScreen.svelte'
|
|
74
80
|
import ContentListScreen from './screens/content/ContentListScreen.svelte'
|
|
75
81
|
import EventsListScreen from './screens/events/EventsListScreen.svelte'
|
|
76
|
-
import ExperienceInstancesListScreen from './screens/experience-instances/ExperienceInstancesListScreen.svelte'
|
|
77
|
-
import ExperiencesListScreen from './screens/experiences/ExperiencesListScreen.svelte'
|
|
78
|
-
import FeaturesListScreen from './screens/features/FeaturesListScreen.svelte'
|
|
79
82
|
import FieldsListScreen from './screens/fields/FieldsListScreen.svelte'
|
|
80
83
|
import FrameworksListScreen from './screens/frameworks/FrameworksListScreen.svelte'
|
|
81
84
|
import HistoryEntriesListScreen from './screens/history/HistoryEntriesListScreen.svelte'
|
|
82
85
|
import IdentityScreen from './screens/identity/IdentityScreen.svelte'
|
|
83
86
|
import PredicatesListScreen from './screens/predicates/PredicatesListScreen.svelte'
|
|
84
|
-
import ResponsesListScreen from './screens/responses/ResponsesListScreen.svelte'
|
|
85
87
|
import CustomToolsScreen from './screens/custom-tools/CustomToolsScreen.svelte'
|
|
86
88
|
|
|
87
89
|
const experience = getExperience()
|
|
@@ -118,22 +120,18 @@
|
|
|
118
120
|
const sessionStorageOf = (): Storage => window.sessionStorage
|
|
119
121
|
const localStorageOf = (): Storage => window.localStorage
|
|
120
122
|
|
|
121
|
-
/** The
|
|
122
|
-
const
|
|
123
|
-
[
|
|
124
|
-
[
|
|
125
|
-
[
|
|
126
|
-
[
|
|
127
|
-
[
|
|
128
|
-
[
|
|
129
|
-
[
|
|
130
|
-
[
|
|
131
|
-
[
|
|
132
|
-
[
|
|
133
|
-
[developerTabId('history')]: HistoryEntriesListScreen,
|
|
134
|
-
[developerTabId('identity')]: IdentityScreen,
|
|
135
|
-
[developerTabId('predicates')]: PredicatesListScreen,
|
|
136
|
-
[developerTabId('responses')]: ResponsesListScreen,
|
|
123
|
+
/** The SDK's own screens, keyed by their sidebar id. */
|
|
124
|
+
const SCREEN_COMPONENTS: Record<string, Component> = {
|
|
125
|
+
[screenTabId('project')]: ProjectScreen,
|
|
126
|
+
[screenTabId('components')]: ComponentsListScreen,
|
|
127
|
+
[screenTabId('config')]: ConfigScreen,
|
|
128
|
+
[screenTabId('content')]: ContentListScreen,
|
|
129
|
+
[screenTabId('events')]: EventsListScreen,
|
|
130
|
+
[screenTabId('fields')]: FieldsListScreen,
|
|
131
|
+
[screenTabId('frameworks')]: FrameworksListScreen,
|
|
132
|
+
[screenTabId('history')]: HistoryEntriesListScreen,
|
|
133
|
+
[screenTabId('identity')]: IdentityScreen,
|
|
134
|
+
[screenTabId('predicates')]: PredicatesListScreen,
|
|
137
135
|
}
|
|
138
136
|
|
|
139
137
|
// ── The session record ───────────────────────────────────────────────────────────────────────
|
|
@@ -169,26 +167,19 @@
|
|
|
169
167
|
)
|
|
170
168
|
const storedTab = readStored(sessionStorageOf, TAB_STORAGE_KEY)
|
|
171
169
|
let tab = $state(storedTab ?? 'guide')
|
|
172
|
-
let developerOpen = $state(readStored(sessionStorageOf, DEVELOPER_OPEN_STORAGE_KEY) === 'true')
|
|
173
|
-
let customOpen = $state(readStored(sessionStorageOf, CUSTOM_TOOLS_OPEN_STORAGE_KEY) !== 'false')
|
|
174
170
|
let drawerOpen = $state(false)
|
|
175
171
|
|
|
176
|
-
// A tab can disappear under the viewer
|
|
177
|
-
// stored developer screen opens its group.
|
|
172
|
+
// A tab can disappear under the viewer: Feature flags once the feature list is empty.
|
|
178
173
|
$effect(() => {
|
|
179
174
|
const resolved = resolveTab(tab, nav)
|
|
180
175
|
if (resolved !== tab) tab = resolved
|
|
181
|
-
if (isInDeveloper(tab, nav) && !developerOpen) developerOpen = true
|
|
182
|
-
if (isInCustomTools(tab, nav) && !customOpen) customOpen = true
|
|
183
176
|
})
|
|
184
177
|
|
|
185
|
-
const selectedItem = $derived(
|
|
186
|
-
[...nav.primary, ...nav.custom, ...nav.developer, ...nav.footer].find((item) => item.id === tab),
|
|
187
|
-
)
|
|
178
|
+
const selectedItem = $derived([...nav.primary, ...nav.footer].find((item) => item.id === tab))
|
|
188
179
|
const selectedTool = $derived(
|
|
189
180
|
[...tools, ...customTools, ...footer].find((tool) => toolTabId(tool.id) === tab),
|
|
190
181
|
)
|
|
191
|
-
const
|
|
182
|
+
const sdkScreen = $derived(SCREEN_COMPONENTS[tab])
|
|
192
183
|
|
|
193
184
|
/**
|
|
194
185
|
* Narrows a tab id to one of the review tabs.
|
|
@@ -213,26 +204,6 @@
|
|
|
213
204
|
writeStored(sessionStorageOf, TAB_STORAGE_KEY, id)
|
|
214
205
|
}
|
|
215
206
|
|
|
216
|
-
/**
|
|
217
|
-
* Opens or folds the Custom Tools group, and remembers it.
|
|
218
|
-
*
|
|
219
|
-
* @example toggleCustom()
|
|
220
|
-
*/
|
|
221
|
-
function toggleCustom(): void {
|
|
222
|
-
customOpen = !customOpen
|
|
223
|
-
writeStored(sessionStorageOf, CUSTOM_TOOLS_OPEN_STORAGE_KEY, String(customOpen))
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Opens or folds the Developer group, and remembers it.
|
|
228
|
-
*
|
|
229
|
-
* @example toggleDeveloper()
|
|
230
|
-
*/
|
|
231
|
-
function toggleDeveloper(): void {
|
|
232
|
-
developerOpen = !developerOpen
|
|
233
|
-
writeStored(sessionStorageOf, DEVELOPER_OPEN_STORAGE_KEY, String(developerOpen))
|
|
234
|
-
}
|
|
235
|
-
|
|
236
207
|
// ── How to use ───────────────────────────────────────────────────────────────────────────────
|
|
237
208
|
const guide = $derived.by(() => {
|
|
238
209
|
const available = { variations: $features.length > 0 }
|
|
@@ -262,7 +233,7 @@
|
|
|
262
233
|
sections: guideSections(available, { lead, tools: toolSections }),
|
|
263
234
|
fixes: guideFixes(available, options.guide.fixes),
|
|
264
235
|
tabLabels: Object.fromEntries(
|
|
265
|
-
[...nav.primary, ...nav.
|
|
236
|
+
[...nav.primary, ...nav.footer].map((item) => [item.id, item.label]),
|
|
266
237
|
),
|
|
267
238
|
}
|
|
268
239
|
})
|
|
@@ -285,14 +256,22 @@
|
|
|
285
256
|
writeAutoHide(localStorageOf, value)
|
|
286
257
|
}
|
|
287
258
|
|
|
288
|
-
// ── A persona
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
//
|
|
259
|
+
// ── A persona session, carried across reloads ──────────────────────────────────────────────
|
|
260
|
+
// Walk through and Jump to step reload the page clean, so the session starts here, as the
|
|
261
|
+
// devtools mount on the new page, and again on every later page of the tab until Stop. Each
|
|
262
|
+
// start runs the persona's setup and pre-fills every step with its responses. A jump walks to
|
|
263
|
+
// its step first, with the panel shut so the reviewer watches the journey move, and opens on the
|
|
264
|
+
// Personas tab when the walk stops short. A shared link starts a walk-through on the page it
|
|
265
|
+
// opens.
|
|
292
266
|
let personaWalkNotice = $state<{
|
|
293
267
|
status: 'info' | 'success' | 'warning' | 'error'
|
|
294
268
|
text: string
|
|
295
269
|
} | null>(null)
|
|
270
|
+
/** The site's personas, once the option has loaded them. */
|
|
271
|
+
let sitePersonaList = $state.raw<Persona[]>([])
|
|
272
|
+
/** The persona this tab walks through as. */
|
|
273
|
+
let activePersona = $state.raw<Persona | null>(null)
|
|
274
|
+
let prefill: Prefill | null = null
|
|
296
275
|
|
|
297
276
|
const walkSettings: WalkSettings = {
|
|
298
277
|
exclude: options.hiddenExperienceKeys,
|
|
@@ -304,30 +283,108 @@
|
|
|
304
283
|
}
|
|
305
284
|
|
|
306
285
|
/**
|
|
307
|
-
*
|
|
286
|
+
* A persona by id, among the site's as this browser edited them and this browser's own.
|
|
287
|
+
*
|
|
288
|
+
* @param {string} id - The persona's id.
|
|
289
|
+
* @param {readonly Persona[]} site - The site's personas.
|
|
290
|
+
* @returns {Persona | undefined} The persona, or `undefined` when none has the id.
|
|
291
|
+
* @example findPersona('mover', sitePersonaList)
|
|
292
|
+
*/
|
|
293
|
+
function findPersona(id: string, site: readonly Persona[]): Persona | undefined {
|
|
294
|
+
const listed = applyEdits(site, readSavedPersonas(localStorageOf, client.projectId))
|
|
295
|
+
return [...listed.site, ...listed.own].find((persona) => persona.id === id)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Walks a jump to its step, then says how it went, opening the panel when it stopped short.
|
|
308
300
|
*
|
|
309
|
-
* @
|
|
310
|
-
* @
|
|
301
|
+
* @param {Persona} persona - The persona.
|
|
302
|
+
* @param {PendingLoad} pending - The jump.
|
|
303
|
+
* @returns {Promise<void>} Settles once the walk ends.
|
|
304
|
+
* @example await jump(persona, pending)
|
|
311
305
|
*/
|
|
312
|
-
function
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
personaWalkNotice =
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
306
|
+
async function jump(persona: Persona, pending: PendingLoad): Promise<void> {
|
|
307
|
+
const stepName = pending.stop?.stepName ?? pending.stop?.stepKey ?? ''
|
|
308
|
+
personaWalkNotice = { status: 'info', text: `Jumping to “${stepName}” as ${persona.name}…` }
|
|
309
|
+
const outcome = await walkPersona(
|
|
310
|
+
client,
|
|
311
|
+
{ ...persona, landing: pending.stop, passed: pending.passed },
|
|
312
|
+
walkSettings,
|
|
313
|
+
)
|
|
314
|
+
const notice = walkNotice(persona, outcome)
|
|
315
|
+
personaWalkNotice = notice
|
|
316
|
+
if (notice.status === 'success') return
|
|
317
|
+
selectTab('personas')
|
|
318
|
+
experience.open()
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Starts or resumes this tab's persona session: a reload's start, a shared link, or the session
|
|
323
|
+
* a reload carried on.
|
|
324
|
+
*
|
|
325
|
+
* @returns {Promise<void>} Settles once the persona is set up and any jump has walked.
|
|
326
|
+
* @example await startPersonaSession()
|
|
327
|
+
*/
|
|
328
|
+
async function startPersonaSession(): Promise<void> {
|
|
329
|
+
const storage = sessionStorageOf()
|
|
330
|
+
const pending = takePendingLoad(storage)
|
|
331
|
+
const linked = takePersonaLink(window.location, (url) => {
|
|
332
|
+
window.history.replaceState(window.history.state, '', url)
|
|
328
333
|
})
|
|
334
|
+
const site = await options.personas()
|
|
335
|
+
sitePersonaList = site
|
|
336
|
+
const id = pending?.id ?? linked ?? readActivePersona(storage)
|
|
337
|
+
if (id === null || !posture.canEditAnswers) return
|
|
338
|
+
const persona = findPersona(id, site)
|
|
339
|
+
if (persona === undefined || (pending === null && linked !== null && persona.source !== 'site')) {
|
|
340
|
+
writeActivePersona(storage, null)
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
writeActivePersona(storage, persona.id)
|
|
345
|
+
activePersona = persona
|
|
346
|
+
try {
|
|
347
|
+
await persona.setup?.(client)
|
|
348
|
+
} catch (error) {
|
|
349
|
+
const detail = error instanceof Error ? ` ${error.message}` : ''
|
|
350
|
+
personaWalkNotice = { status: 'error', text: `${persona.name}’s setup failed.${detail}` }
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
prefill = prefillPersona(
|
|
354
|
+
client,
|
|
355
|
+
{ ...persona.responses, ...options.personaWalk.responses },
|
|
356
|
+
session.askedStore,
|
|
357
|
+
)
|
|
358
|
+
if (pending?.stop != null) await jump(persona, pending)
|
|
359
|
+
else if (pending !== null || linked !== null) {
|
|
360
|
+
await openPersonaJourney(client, persona, walkSettings)
|
|
361
|
+
}
|
|
329
362
|
}
|
|
330
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Ends this tab's persona session: every field gets its own default back.
|
|
366
|
+
*
|
|
367
|
+
* @example stopPersonaSession()
|
|
368
|
+
*/
|
|
369
|
+
function stopPersonaSession(): void {
|
|
370
|
+
prefill?.stop()
|
|
371
|
+
prefill = null
|
|
372
|
+
writeActivePersona(sessionStorageOf(), null)
|
|
373
|
+
if (activePersona !== null) {
|
|
374
|
+
personaWalkNotice = {
|
|
375
|
+
status: 'success',
|
|
376
|
+
text: `Stopped walking through as ${activePersona.name}. Fields already filled in keep their values.`,
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
activePersona = null
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
void startPersonaSession()
|
|
384
|
+
$effect(() => () => {
|
|
385
|
+
prefill?.stop()
|
|
386
|
+
})
|
|
387
|
+
|
|
331
388
|
// ── Dialog behaviour ─────────────────────────────────────────────────────────────────────────
|
|
332
389
|
/** The open panel; Svelte sets it back to `null` when the panel unmounts. */
|
|
333
390
|
let panel: HTMLDivElement | null = $state(null)
|
|
@@ -453,15 +510,7 @@
|
|
|
453
510
|
<div class="drawer-scrim" aria-hidden="true" onclick={() => (drawerOpen = false)}></div>
|
|
454
511
|
{/if}
|
|
455
512
|
<div class="nav-column" class:drawer-open={drawerOpen} id="devtools-nav">
|
|
456
|
-
<Sidebar
|
|
457
|
-
{nav}
|
|
458
|
-
selected={tab}
|
|
459
|
-
{developerOpen}
|
|
460
|
-
{customOpen}
|
|
461
|
-
onSelect={selectTab}
|
|
462
|
-
onToggleDeveloper={toggleDeveloper}
|
|
463
|
-
onToggleCustom={toggleCustom}
|
|
464
|
-
/>
|
|
513
|
+
<Sidebar {nav} selected={tab} onSelect={selectTab} />
|
|
465
514
|
<SidebarFooter
|
|
466
515
|
tools={nav.footer}
|
|
467
516
|
selected={tab}
|
|
@@ -472,9 +521,9 @@
|
|
|
472
521
|
</div>
|
|
473
522
|
|
|
474
523
|
<section class="pane" aria-label={selectedItem?.label} data-testid="devtools-pane">
|
|
475
|
-
{#if
|
|
476
|
-
{@const Screen =
|
|
477
|
-
<div class="
|
|
524
|
+
{#if sdkScreen}
|
|
525
|
+
{@const Screen = sdkScreen}
|
|
526
|
+
<div class="screen-body">
|
|
478
527
|
<Screen />
|
|
479
528
|
</div>
|
|
480
529
|
{:else if tab === CUSTOM_TOOLS_TAB}
|
|
@@ -533,13 +582,15 @@
|
|
|
533
582
|
{client}
|
|
534
583
|
{posture}
|
|
535
584
|
journey={$journey}
|
|
536
|
-
personas={
|
|
585
|
+
personas={sitePersonaList}
|
|
537
586
|
asked={$asked}
|
|
538
587
|
route={$route}
|
|
539
588
|
hiddenKeys={options.hiddenExperienceKeys}
|
|
540
589
|
codeOwned={options.codeOwnedFields}
|
|
541
590
|
personaWalk={options.personaWalk}
|
|
542
591
|
tools={personaTools}
|
|
592
|
+
{activePersona}
|
|
593
|
+
onStop={stopPersonaSession}
|
|
543
594
|
walkNotice={personaWalkNotice}
|
|
544
595
|
onWalkNoticeDismiss={() => (personaWalkNotice = null)}
|
|
545
596
|
/>
|
|
@@ -557,6 +608,7 @@
|
|
|
557
608
|
position={options.launcher.position}
|
|
558
609
|
{autoHide}
|
|
559
610
|
{preview}
|
|
611
|
+
walkingAs={activePersona?.name ?? null}
|
|
560
612
|
onOpen={() => experience.open()}
|
|
561
613
|
/>
|
|
562
614
|
{/if}
|
|
@@ -824,7 +876,7 @@
|
|
|
824
876
|
display: grid;
|
|
825
877
|
}
|
|
826
878
|
|
|
827
|
-
.
|
|
879
|
+
.screen-body {
|
|
828
880
|
flex: 1 1 auto;
|
|
829
881
|
display: grid;
|
|
830
882
|
min-height: 0;
|
|
@@ -28,7 +28,7 @@ export interface CustomTool {
|
|
|
28
28
|
* `'review'` lists the tool as a tab of its own, beside the review tabs. `'personas'` shows it
|
|
29
29
|
* as a section of the Personas & Scenarios tab, under its label. `'footer'` shows it as an icon
|
|
30
30
|
* at the foot of the sidebar, beside the auto-hide switch, with its label as a tooltip.
|
|
31
|
-
* `'developer'` lists it as a row of its own
|
|
31
|
+
* `'developer'` lists it as a top-level row of its own, after the review-placed tools.
|
|
32
32
|
*
|
|
33
33
|
* @default 'developer'
|
|
34
34
|
*/
|
|
@@ -82,7 +82,7 @@ export declare function personasTools(tools: readonly CustomTool[]): CustomTool[
|
|
|
82
82
|
*/
|
|
83
83
|
export declare function footerTools(tools: readonly CustomTool[]): CustomTool[];
|
|
84
84
|
/**
|
|
85
|
-
* The tools listed
|
|
85
|
+
* The tools listed as top-level rows after the review-placed tools.
|
|
86
86
|
*
|
|
87
87
|
* @param {readonly CustomTool[]} tools - The registered tools.
|
|
88
88
|
* @returns {CustomTool[]} The unplaced tools and those placed `'developer'`, in registration order.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=[{id:`session`,title:`See where you are`,steps:[],notes:[`To see the step you are on and every response so far, open **This session**.`],tab:`session`},{id:`answers`,title:`Change a response`,steps:[`Open **Responses**.`,`Find the field. Use **With a response**, **No response** or **All** to narrow the list.`,`Click **Change**, or **Add** if it has no response yet.`,`Pick or type the new response, then click **Save**.`],notes:[`To remove one response, click **Clear** beside it.`,`To start the whole experience again, click **Start over**, then **Clear everything**.`,`If a field says **Change it on its step**, go back to that step and change it there.`],tab:`answers`},{id:`journey`,title:`Go back to an earlier step`,steps:[`Open **
|
|
1
|
+
var e=[{id:`session`,title:`See where you are`,steps:[],notes:[`To see the step you are on and every response so far, open **This session**.`],tab:`session`},{id:`answers`,title:`Change a response`,steps:[`Open **Responses**.`,`Find the field. Use **With a response**, **No response** or **All** to narrow the list.`,`Click **Change**, or **Add** if it has no response yet.`,`Pick or type the new response, then click **Save**.`],notes:[`To remove one response, click **Clear** beside it.`,`To start the whole experience again, click **Start over**, then **Clear everything**.`,`If a field says **Change it on its step**, go back to that step and change it there.`],tab:`answers`},{id:`journey`,title:`Go back to an earlier step`,steps:[`Open **Experiences**.`,`Find a step on your route. Your route is blue, and your step says **You are here**.`,`Click the step, then click **Go back to this step**.`],notes:[`You can only go back to steps you have already visited.`,`To change how the flow is drawn, pick **Map**, **Timeline** or **Outline** at the top.`,`To see where an experience shows and when it starts, pick **Details**.`,`To open an experience that is not on the page, pick it, then click **Launch experience**.`],tab:`journey`},{id:`personas`,title:`Try the experience as a visitor`,steps:[`Open **Personas & Scenarios**.`,`Click **Walk through** beside the visitor you want. The page starts again.`,`Go through the experience. Each step shows their responses. Click **Continue**, or change a response first.`,`When you are done, click **Stop** at the top of the tab.`],notes:[`To start further in, click **Jump to step**, then pick the step.`,`To change a persona, click **Edit**. Your change stays in this browser only.`,`To keep where you are for later, click **Create**, give it a name, then click **Save**.`,`To send a site persona to someone else, click its copy icon and share the link.`],tab:`personas`},{id:`variations`,title:`View a feature flag variant`,steps:[`Open **Feature flags**.`,`Find the feature flag and pick the variant you want.`,`Click **Show me this variant**. The page reloads showing it.`,`To share exactly what you saw, click **Copy link** and send it.`],notes:[`To go back to normal for that feature flag, click **Leave this one alone**.`],tab:`variations`}],t=[{tab:`journey`,entry:{problem:`I can't click a step in the Experiences tab`,fix:`Go back only to steps you have visited.`}}];function n(e,t){return e!==`variations`||t.variations}function r(t,r){return[...r?.lead??[],...e.filter(e=>n(e.tab,t)),...r?.tools??[]]}function i(e,r){return[...t.filter(t=>n(t.tab,e)).map(e=>e.entry),...r??[]]}function a(e){let t=e.split(`**`);return t.length%2==0?[{text:e,bold:!1}]:t.map((e,t)=>({text:e,bold:t%2==1})).filter(e=>e.text.length>0)}export{a as emphasis,i as guideFixes,r as guideSections};
|
|
@@ -16,12 +16,12 @@ export { COLOURS, LANE_WIDTH, ROW_HEIGHT, buildTimeline } from './journey-timeli
|
|
|
16
16
|
export type { TimelineEdge, TimelineLayout, TimelineNext, TimelineNode } from './journey-timeline';
|
|
17
17
|
export { BREADTH, DEPTH, buildTree } from './journey-tree';
|
|
18
18
|
export type { FlowEnd, TreeInput, TreeJump, TreeLayout, TreeLink, TreeNode, TreeNodeKind, } from './journey-tree';
|
|
19
|
-
export {
|
|
19
|
+
export { NODE_LABELS, TAB_INTROS, TAB_LABELS, configSourceLabel, deslug, screenName, } from './plain-language';
|
|
20
20
|
export type { ReviewTabId } from './plain-language';
|
|
21
21
|
export { builderGraph, responseKeysOf, walkDraft } from './persona-builder';
|
|
22
22
|
export type { BuilderCondition, BuilderEdge, BuilderField, BuilderInput, BuilderJourney, BuilderNode, BuilderRow, BuilderStep, BuilderWalk, GraphSource, } from './persona-builder';
|
|
23
|
-
export { PERSONA_STORAGE_PREFIX, buildPersona, customerAnswers, describeLanding, entryUrlOf, passedScreens, personaStorageKey, readAnswers, readEntryUrl, readSavedPersonas, sitePersonas, writeSavedPersonas, } from './personas';
|
|
24
|
-
export type { BuildPersonaResult, Persona, PersonaCapture, PersonaDefinition, PersonaDraft,
|
|
23
|
+
export { PERSONA_STORAGE_PREFIX, applyEdits, buildPersona, customerAnswers, describeLanding, entryUrlOf, groupPersonas, matchesSearch, passedScreens, personaStorageKey, readAnswers, readEntryUrl, readLanding, readPassed, readSavedPersonas, sitePersonas, startOf, writeSavedPersonas, } from './personas';
|
|
24
|
+
export type { BuildPersonaResult, EditedPersonas, Persona, PersonaCapture, PersonaDefinition, PersonaDraft, PersonaGroup, PersonaLanding, PersonaScreen, PersonaSetup, PersonaSource, PersonaStorageGetter, } from './personas';
|
|
25
25
|
export { UNDESCRIBABLE, describeRule, varKeyOf } from './plain-logic';
|
|
26
26
|
export type { FieldLookup, FieldSummary } from './plain-logic';
|
|
27
27
|
export { derivePosture } from './safety-posture';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isBlank as e,nextHistory as t,submittedAnswers as n}from"./answer-history/answer-history.js";import{
|
|
1
|
+
import{isBlank as e,nextHistory as t,submittedAnswers as n}from"./answer-history/answer-history.js";import{NODE_LABELS as r,TAB_INTROS as i,TAB_LABELS as a,configSourceLabel as o,deslug as s,screenName as c}from"./plain-language/plain-language.js";import{PERSONA_STORAGE_PREFIX as l,applyEdits as u,buildPersona as d,customerAnswers as f,describeLanding as p,entryUrlOf as m,groupPersonas as h,matchesSearch as g,passedScreens as _,personaStorageKey as v,readAnswers as y,readEntryUrl as b,readLanding as x,readPassed as S,readSavedPersonas as C,sitePersonas as w,startOf as T,writeSavedPersonas as E}from"./personas/personas.js";import{askedFieldKeys as D,describeAnswer as O,kindOf as k,nounOf as A,questionOf as j,renderAnswer as M,systemPayload as N}from"./answer-display/answer-display.js";import{emphasis as P,guideFixes as F,guideSections as I}from"./guide-sections/guide-sections.js";import{UNDESCRIBABLE as L,describeRule as R,varKeyOf as z}from"./plain-logic/plain-logic.js";import{buildOutline as B}from"./journey-outline/journey-outline.js";import{nextOpenOrder as V,pickJourney as H}from"./journey-pick/journey-pick.js";import{BREADTH as U,DEPTH as W,buildTree as G}from"./journey-tree/journey-tree.js";import"./journey-tree/index.js";import{COLOURS as K,LANE_WIDTH as q,ROW_HEIGHT as J,buildTimeline as Y}from"./journey-timeline/journey-timeline.js";import"./journey-timeline/index.js";import{builderGraph as X,responseKeysOf as Z,walkDraft as Q}from"./persona-builder/persona-builder.js";import{derivePosture as $}from"./safety-posture/safety-posture.js";import{nextTrails as ee}from"./visited-steps/visited-steps.js";import{nextRoute as te}from"./walk-route/walk-route.js";export{U as BREADTH,K as COLOURS,W as DEPTH,q as LANE_WIDTH,r as NODE_LABELS,l as PERSONA_STORAGE_PREFIX,J as ROW_HEIGHT,i as TAB_INTROS,a as TAB_LABELS,L as UNDESCRIBABLE,u as applyEdits,D as askedFieldKeys,B as buildOutline,d as buildPersona,Y as buildTimeline,G as buildTree,X as builderGraph,o as configSourceLabel,f as customerAnswers,$ as derivePosture,O as describeAnswer,p as describeLanding,R as describeRule,s as deslug,P as emphasis,m as entryUrlOf,h as groupPersonas,F as guideFixes,I as guideSections,e as isBlank,k as kindOf,g as matchesSearch,t as nextHistory,V as nextOpenOrder,te as nextRoute,ee as nextTrails,A as nounOf,_ as passedScreens,v as personaStorageKey,H as pickJourney,j as questionOf,y as readAnswers,b as readEntryUrl,x as readLanding,S as readPassed,C as readSavedPersonas,M as renderAnswer,Z as responseKeysOf,c as screenName,w as sitePersonas,T as startOf,n as submittedAnswers,N as systemPayload,z as varKeyOf,Q as walkDraft,E as writeSavedPersonas};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** @file Public entrypoint for personas: building, saving, reading and describing them. */
|
|
2
|
-
export { PERSONA_STORAGE_PREFIX, buildPersona, customerAnswers, describeLanding, entryUrlOf, passedScreens, personaStorageKey, readAnswers, readEntryUrl, readSavedPersonas, sitePersonas, writeSavedPersonas, } from './personas';
|
|
3
|
-
export type { BuildPersonaResult, Persona, PersonaCapture, PersonaDefinition, PersonaDraft,
|
|
2
|
+
export { PERSONA_STORAGE_PREFIX, applyEdits, buildPersona, customerAnswers, describeLanding, entryUrlOf, groupPersonas, matchesSearch, passedScreens, personaStorageKey, readAnswers, readEntryUrl, readLanding, readPassed, readSavedPersonas, sitePersonas, startOf, writeSavedPersonas, } from './personas';
|
|
3
|
+
export type { BuildPersonaResult, EditedPersonas, Persona, PersonaCapture, PersonaDefinition, PersonaDraft, PersonaGroup, PersonaLanding, PersonaScreen, PersonaSetup, PersonaSource, PersonaStorageGetter, } from './personas';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{PERSONA_STORAGE_PREFIX,buildPersona,customerAnswers,describeLanding,entryUrlOf,passedScreens,personaStorageKey,readAnswers,readEntryUrl,readSavedPersonas,sitePersonas,writeSavedPersonas}from"./personas.js";
|
|
1
|
+
export{PERSONA_STORAGE_PREFIX,applyEdits,buildPersona,customerAnswers,describeLanding,entryUrlOf,groupPersonas,matchesSearch,passedScreens,personaStorageKey,readAnswers,readEntryUrl,readLanding,readPassed,readSavedPersonas,sitePersonas,startOf,writeSavedPersonas}from"./personas.js";
|
|
@@ -20,18 +20,28 @@ export interface PersonaScreen {
|
|
|
20
20
|
experienceKey: string;
|
|
21
21
|
stepKey: string;
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
24
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Puts the page in the state a persona needs before its experience starts, such as a stubbed
|
|
25
|
+
* account or a location. It runs on every page of the persona's session, before the walk.
|
|
26
|
+
*/
|
|
27
|
+
export type PersonaSetup = (client: Picked) => void | Promise<void>;
|
|
25
28
|
/** A customer or situation to try the journey as. */
|
|
26
29
|
export interface Persona {
|
|
27
30
|
id: string;
|
|
28
31
|
name: string;
|
|
29
32
|
description: string | null;
|
|
30
33
|
source: PersonaSource;
|
|
34
|
+
/** The heading it is listed under; `null` lists it under its source's heading. */
|
|
35
|
+
group: string | null;
|
|
31
36
|
/** The answers in place once it loads. */
|
|
32
37
|
responses: Readonly<Record<string, FieldValue>>;
|
|
33
38
|
/** Where a load stops; `null` loads the answers only. */
|
|
34
39
|
landing: PersonaLanding | null;
|
|
40
|
+
/**
|
|
41
|
+
* The experience a walk starts in, such as the page's entry experience. `null` starts in the
|
|
42
|
+
* experience of the first screen moved on from, else the landing's.
|
|
43
|
+
*/
|
|
44
|
+
start: string | null;
|
|
35
45
|
/**
|
|
36
46
|
* The screens moved on from when it was saved, in the order first left. A load presses Continue
|
|
37
47
|
* on a screen that asks nothing only when it is listed here.
|
|
@@ -49,8 +59,10 @@ export interface Persona {
|
|
|
49
59
|
entryUrl: string | null;
|
|
50
60
|
/** When it was saved, as an ISO time; `null` for a host-defined persona. */
|
|
51
61
|
savedAt: string | null;
|
|
52
|
-
/** The host's
|
|
53
|
-
|
|
62
|
+
/** The host's setup; `null` when it needs none. */
|
|
63
|
+
setup: PersonaSetup | null;
|
|
64
|
+
/** `true` for a host-defined persona that a reviewer edited in this browser. */
|
|
65
|
+
edited: boolean;
|
|
54
66
|
}
|
|
55
67
|
/** A persona as the host defines it in the plugin's `personas` option. */
|
|
56
68
|
export interface PersonaDefinition {
|
|
@@ -59,16 +71,20 @@ export interface PersonaDefinition {
|
|
|
59
71
|
name: string;
|
|
60
72
|
/** Who they are, in a sentence. */
|
|
61
73
|
description?: string;
|
|
74
|
+
/** The heading it is listed under, such as `Moving`. */
|
|
75
|
+
group?: string;
|
|
62
76
|
/** The answers in place once it loads. */
|
|
63
77
|
responses?: Readonly<Record<string, FieldValue>>;
|
|
64
78
|
/** The journey it opens. */
|
|
65
79
|
experienceKey?: string;
|
|
66
80
|
/** The screen in that journey it lands on. */
|
|
67
81
|
stepKey?: string;
|
|
82
|
+
/** The experience a walk starts in, such as the page's entry experience; `experienceKey` when absent. */
|
|
83
|
+
startExperienceKey?: string;
|
|
68
84
|
/** The page a load starts on, such as a campaign landing; the page in front when absent. */
|
|
69
85
|
entryUrl?: string;
|
|
70
|
-
/**
|
|
71
|
-
|
|
86
|
+
/** Prepares the page for it, such as stubbing an account, before each walk. */
|
|
87
|
+
setup?: PersonaSetup;
|
|
72
88
|
}
|
|
73
89
|
/** What the reviewer types when saving where they are. */
|
|
74
90
|
export interface PersonaDraft {
|
|
@@ -117,6 +133,22 @@ export declare function personaStorageKey(projectId: string): string;
|
|
|
117
133
|
* @example readAnswers({ age: 42 }) // { age: 42 }
|
|
118
134
|
*/
|
|
119
135
|
export declare function readAnswers(value: unknown): Record<string, FieldValue>;
|
|
136
|
+
/**
|
|
137
|
+
* Reads a stored landing.
|
|
138
|
+
*
|
|
139
|
+
* @param {unknown} value - The value to read.
|
|
140
|
+
* @returns {PersonaLanding | null} The landing, or `null` when it names no journey.
|
|
141
|
+
* @example readLanding({ experienceKey: 'needs-analysis' })
|
|
142
|
+
*/
|
|
143
|
+
export declare function readLanding(value: unknown): PersonaLanding | null;
|
|
144
|
+
/**
|
|
145
|
+
* Reads a stored list of passed screens, keeping only the well-formed entries.
|
|
146
|
+
*
|
|
147
|
+
* @param {unknown} value - The stored list.
|
|
148
|
+
* @returns {PersonaScreen[]} The screens, in order.
|
|
149
|
+
* @example readPassed([{ experienceKey: 'funnel', stepKey: 'postcode' }])
|
|
150
|
+
*/
|
|
151
|
+
export declare function readPassed(value: unknown): PersonaScreen[];
|
|
120
152
|
/**
|
|
121
153
|
* Reads an entry URL, or `null` when it is not a path on this site.
|
|
122
154
|
*
|
|
@@ -186,7 +218,7 @@ export declare function customerAnswers(responses: Readonly<Record<string, Field
|
|
|
186
218
|
*/
|
|
187
219
|
export declare function buildPersona(draft: PersonaDraft, capture: PersonaCapture): BuildPersonaResult;
|
|
188
220
|
/**
|
|
189
|
-
* Reads the host's persona definitions, dropping any without an id or name. A host
|
|
221
|
+
* Reads the host's persona definitions, dropping any without an id or name. A host setup is
|
|
190
222
|
* wrapped so it can be any function the host passed.
|
|
191
223
|
*
|
|
192
224
|
* @param {unknown} definitions - The plugin's `personas` option.
|
|
@@ -202,3 +234,52 @@ export declare function sitePersonas(definitions: unknown): Persona[];
|
|
|
202
234
|
* @example describeLanding(null) // 'Responses only'
|
|
203
235
|
*/
|
|
204
236
|
export declare function describeLanding(landing: PersonaLanding | null): string;
|
|
237
|
+
/**
|
|
238
|
+
* The experience a persona's walk starts in.
|
|
239
|
+
*
|
|
240
|
+
* @param {Pick<Persona, 'passed' | 'start' | 'landing'>} persona - The persona.
|
|
241
|
+
* @returns {string | null} The experience key, or `null` for a persona with no journey.
|
|
242
|
+
* @example startOf(persona) // 'needs-analysis'
|
|
243
|
+
*/
|
|
244
|
+
export declare function startOf(persona: Pick<Persona, 'passed' | 'start' | 'landing'>): string | null;
|
|
245
|
+
/** The host's personas with this browser's edits applied, and the reviewer's own. */
|
|
246
|
+
export interface EditedPersonas {
|
|
247
|
+
/** The host's personas, each replaced by its edit when this browser holds one. */
|
|
248
|
+
site: Persona[];
|
|
249
|
+
/** The personas this browser saved that edit none of the host's. */
|
|
250
|
+
own: Persona[];
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Applies this browser's edits to the host's personas. An edit is a saved persona with a host
|
|
254
|
+
* persona's id; it keeps the host's heading, start and setup.
|
|
255
|
+
*
|
|
256
|
+
* @param {readonly Persona[]} site - The host's personas.
|
|
257
|
+
* @param {readonly Persona[]} saved - The personas saved in this browser.
|
|
258
|
+
* @returns {EditedPersonas} The host's personas as edited, and the reviewer's own.
|
|
259
|
+
* @example applyEdits(options.personas, readSavedPersonas(getStorage, projectId))
|
|
260
|
+
*/
|
|
261
|
+
export declare function applyEdits(site: readonly Persona[], saved: readonly Persona[]): EditedPersonas;
|
|
262
|
+
/** Personas listed under one heading. */
|
|
263
|
+
export interface PersonaGroup {
|
|
264
|
+
title: string;
|
|
265
|
+
personas: Persona[];
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Lists personas under their headings, in the order each heading first appears.
|
|
269
|
+
*
|
|
270
|
+
* @param {readonly Persona[]} personas - The personas.
|
|
271
|
+
* @param {string} fallback - The heading for a persona with no group.
|
|
272
|
+
* @returns {PersonaGroup[]} The headings and their personas.
|
|
273
|
+
* @example groupPersonas(personas, 'For this site')
|
|
274
|
+
*/
|
|
275
|
+
export declare function groupPersonas(personas: readonly Persona[], fallback: string): PersonaGroup[];
|
|
276
|
+
/**
|
|
277
|
+
* Whether a persona matches what the reviewer searched for: every word appears in its name,
|
|
278
|
+
* description or heading.
|
|
279
|
+
*
|
|
280
|
+
* @param {Persona} persona - The persona.
|
|
281
|
+
* @param {string} search - What was typed.
|
|
282
|
+
* @returns {boolean} `true` when it matches, and always for an empty search.
|
|
283
|
+
* @example matchesSearch(persona, 'moving gas')
|
|
284
|
+
*/
|
|
285
|
+
export declare function matchesSearch(persona: Persona, search: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isBlank as e}from"../answer-history/answer-history.js";import{deslug as t}from"../plain-language/plain-language.js";var n=`picked-devtools-personas`,r=1;function i(e){return`${n}:${e}`}function a(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function o(e){return e===null||typeof e==`string`||typeof e==`boolean`?!0:typeof e==`number`?Number.isFinite(e):Array.isArray(e)?e.every(o):a(e)&&Object.values(e).every(o)}function s(e){if(!a(e))return{};let t={};for(let[n,r]of Object.entries(e))o(r)&&(t[n]=r);return t}function c(e){if(typeof e!=`string`)return null;let t=e.trim();return t.length>0?t:null}function l(e){if(!a(e))return null;let t=c(e.experienceKey);return t===null?null:{experienceKey:t,experienceName:c(e.experienceName),stepKey:c(e.stepKey),stepName:c(e.stepName)}}function u(e){return Array.isArray(e)?e.flatMap(e=>{if(!a(e))return[];let t=c(e.experienceKey),n=c(e.stepKey);return t===null||n===null?[]:[{experienceKey:t,stepKey:n}]}):[]}function d(e){if(!a(e))return{};let t={};for(let[n,r]of Object.entries(e))typeof r==`boolean`&&(t[n]=r);return t}function f(e){let t=c(e);return t!==null&&t.startsWith(`/`)&&!t.startsWith(`//`)?t:null}function p(e,t){let n=e.trim();if(n.length===0)return null;try{let e=new URL(n,t);return e.origin===t?`${e.pathname}${e.search}`:null}catch{return null}}function m(e){if(!a(e))return null;let t=c(e.id),n=c(e.name);return t===null||n===null?null:{id:t,name:n,description:c(e.description),source:`saved`,responses:s(e.responses),landing:l(e.landing),passed:u(e.passed),choices:d(e.choices),entryUrl:f(e.entryUrl),savedAt:c(e.savedAt),
|
|
1
|
+
import{isBlank as e}from"../answer-history/answer-history.js";import{deslug as t}from"../plain-language/plain-language.js";var n=`picked-devtools-personas`,r=1;function i(e){return`${n}:${e}`}function a(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function o(e){return e===null||typeof e==`string`||typeof e==`boolean`?!0:typeof e==`number`?Number.isFinite(e):Array.isArray(e)?e.every(o):a(e)&&Object.values(e).every(o)}function s(e){if(!a(e))return{};let t={};for(let[n,r]of Object.entries(e))o(r)&&(t[n]=r);return t}function c(e){if(typeof e!=`string`)return null;let t=e.trim();return t.length>0?t:null}function l(e){if(!a(e))return null;let t=c(e.experienceKey);return t===null?null:{experienceKey:t,experienceName:c(e.experienceName),stepKey:c(e.stepKey),stepName:c(e.stepName)}}function u(e){return Array.isArray(e)?e.flatMap(e=>{if(!a(e))return[];let t=c(e.experienceKey),n=c(e.stepKey);return t===null||n===null?[]:[{experienceKey:t,stepKey:n}]}):[]}function d(e){if(!a(e))return{};let t={};for(let[n,r]of Object.entries(e))typeof r==`boolean`&&(t[n]=r);return t}function f(e){let t=c(e);return t!==null&&t.startsWith(`/`)&&!t.startsWith(`//`)?t:null}function p(e,t){let n=e.trim();if(n.length===0)return null;try{let e=new URL(n,t);return e.origin===t?`${e.pathname}${e.search}`:null}catch{return null}}function m(e){if(!a(e))return null;let t=c(e.id),n=c(e.name);return t===null||n===null?null:{id:t,name:n,description:c(e.description),source:`saved`,group:null,responses:s(e.responses),landing:l(e.landing),start:null,passed:u(e.passed),choices:d(e.choices),entryUrl:f(e.entryUrl),savedAt:c(e.savedAt),setup:null,edited:!1}}function h(e,t){let n;try{n=e().getItem(i(t))}catch{return[]}if(n===null)return[];let r;try{r=JSON.parse(n)}catch{return[]}return!a(r)||!Array.isArray(r.personas)?[]:r.personas.map(m).filter(e=>e!==null)}function g(e,t,n){let a=n.map(e=>({id:e.id,name:e.name,description:e.description,responses:e.responses,landing:e.landing,passed:e.passed,choices:e.choices,entryUrl:e.entryUrl,savedAt:e.savedAt}));try{return e().setItem(i(t),JSON.stringify({version:r,personas:a})),!0}catch{return!1}}function _(e,t){let n=new Set,r=[];for(let{journey:i,screen:a}of e){let e=`${i}/${a}`;t.has(i)||n.has(e)||(n.add(e),r.push({experienceKey:i,stepKey:a}))}return r}function v(t,n){let r={};for(let[i,a]of Object.entries(t))n.has(i)&&!e(a)&&(r[i]=a);return r}function y(e,t){let n=c(e.name);return n===null?{ok:!1,error:`Give it a name.`}:Object.keys(t.responses).length===0&&t.journey===null?{ok:!1,error:`Nothing to save yet. Give a response first.`}:{ok:!0,persona:{id:t.id,name:n,description:c(e.description),source:`saved`,group:null,responses:s(t.responses),landing:t.journey,start:null,passed:t.passed,choices:d(t.choices),entryUrl:f(t.entryUrl),savedAt:t.savedAt,setup:null,edited:!1}}}function b(e){return Array.isArray(e)?e.flatMap(e=>{if(!a(e))return[];let t=c(e.id),n=c(e.name);if(t===null||n===null)return[];let r=c(e.experienceKey),i=e.setup;return[{id:t,name:n,description:c(e.description),source:`site`,group:c(e.group),responses:s(e.responses),landing:r===null?null:{experienceKey:r,experienceName:null,stepKey:c(e.stepKey),stepName:null},start:c(e.startExperienceKey),passed:[],choices:{},entryUrl:f(e.entryUrl),savedAt:null,setup:typeof i==`function`?async e=>{await Reflect.apply(i,void 0,[e])}:null,edited:!1}]}):[]}function x(e){if(e===null)return`Responses only`;let n=e.experienceName??t(e.experienceKey);return e.stepKey===null?`Opens ${n}`:`Lands on ${e.stepName??t(e.stepKey)} in ${n}`}function S(e){return e.passed.at(0)?.experienceKey??e.start??e.landing?.experienceKey??null}function C(e,t){let n=new Map(t.map(e=>[e.id,e])),r=new Set(e.map(e=>e.id));return{site:e.map(e=>{let t=n.get(e.id);return t===void 0?e:{...t,source:`site`,group:e.group,start:e.start,setup:e.setup,edited:!0}}),own:t.filter(e=>!r.has(e.id))}}function w(e,t){let n=new Map;for(let r of e){let e=r.group??t;n.set(e,[...n.get(e)??[],r])}return[...n].map(([e,t])=>({title:e,personas:t}))}function T(e,t){let n=[e.name,e.description,e.group].join(` `).toLowerCase();return t.toLowerCase().split(/\s+/).filter(e=>e.length>0).every(e=>n.includes(e))}export{n as PERSONA_STORAGE_PREFIX,C as applyEdits,y as buildPersona,v as customerAnswers,x as describeLanding,p as entryUrlOf,w as groupPersonas,T as matchesSearch,_ as passedScreens,i as personaStorageKey,s as readAnswers,f as readEntryUrl,l as readLanding,u as readPassed,h as readSavedPersonas,b as sitePersonas,S as startOf,g as writeSavedPersonas};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** @file Public entrypoint for the review tools' vocabulary contract. */
|
|
2
|
-
export {
|
|
2
|
+
export { NODE_LABELS, TAB_INTROS, TAB_LABELS, configSourceLabel, deslug, screenName, } from './plain-language';
|
|
3
3
|
export type { ReviewTabId } from './plain-language';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{NODE_LABELS,TAB_INTROS,TAB_LABELS,configSourceLabel,deslug,screenName}from"./plain-language.js";
|