@picked/svelte 0.0.1-dev.1793772853474 → 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.
Files changed (62) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/plugins/devtools/DevToolsExperience.svelte +169 -84
  3. package/dist/plugins/devtools/components/AdaptedToolHost.svelte +9 -3
  4. package/dist/plugins/devtools/customTools.d.ts +16 -7
  5. package/dist/plugins/devtools/customTools.js +1 -1
  6. package/dist/plugins/devtools/derive/answer-display/answer-display.d.ts +1 -1
  7. package/dist/plugins/devtools/derive/answer-display/answer-display.js +1 -1
  8. package/dist/plugins/devtools/derive/guide-sections/guide-sections.js +1 -1
  9. package/dist/plugins/devtools/derive/index.d.ts +3 -3
  10. package/dist/plugins/devtools/derive/index.js +1 -1
  11. package/dist/plugins/devtools/derive/journey-outline/journey-outline.js +1 -1
  12. package/dist/plugins/devtools/derive/journey-tree/journey-tree.js +1 -1
  13. package/dist/plugins/devtools/derive/persona-builder/persona-builder.js +1 -1
  14. package/dist/plugins/devtools/derive/personas/index.d.ts +2 -2
  15. package/dist/plugins/devtools/derive/personas/index.js +1 -1
  16. package/dist/plugins/devtools/derive/personas/personas.d.ts +89 -8
  17. package/dist/plugins/devtools/derive/personas/personas.js +1 -1
  18. package/dist/plugins/devtools/derive/plain-language/index.d.ts +1 -1
  19. package/dist/plugins/devtools/derive/plain-language/index.js +1 -1
  20. package/dist/plugins/devtools/derive/plain-language/plain-language.d.ts +20 -25
  21. package/dist/plugins/devtools/derive/plain-language/plain-language.js +1 -1
  22. package/dist/plugins/devtools/derive/plain-logic/plain-logic.d.ts +1 -1
  23. package/dist/plugins/devtools/derive/plain-logic/plain-logic.js +1 -1
  24. package/dist/plugins/devtools/derive/safety-posture/safety-posture.js +1 -1
  25. package/dist/plugins/devtools/index.d.ts +1 -1
  26. package/dist/plugins/devtools/launcher/Launcher.svelte +13 -4
  27. package/dist/plugins/devtools/options.d.ts +27 -24
  28. package/dist/plugins/devtools/options.js +1 -1
  29. package/dist/plugins/devtools/review/answers/AnswersPanel.svelte +14 -14
  30. package/dist/plugins/devtools/review/guide/GuidePanel.svelte +1 -1
  31. package/dist/plugins/devtools/review/index.d.ts +2 -2
  32. package/dist/plugins/devtools/review/journey/ExperienceDetails.svelte +129 -0
  33. package/dist/plugins/devtools/review/journey/JourneyMap.svelte +16 -14
  34. package/dist/plugins/devtools/review/journey/JourneyPanel.svelte +70 -24
  35. package/dist/plugins/devtools/review/journey/JourneyTimeline.svelte +22 -21
  36. package/dist/plugins/devtools/review/personas/PersonaBuilder.svelte +15 -46
  37. package/dist/plugins/devtools/review/personas/PersonaCard.svelte +227 -44
  38. package/dist/plugins/devtools/review/personas/PersonasPanel.svelte +191 -78
  39. package/dist/plugins/devtools/review/personas/load-persona.d.ts +43 -22
  40. package/dist/plugins/devtools/review/personas/load-persona.js +1 -1
  41. package/dist/plugins/devtools/review/personas/persona-route.d.ts +87 -0
  42. package/dist/plugins/devtools/review/personas/persona-route.js +1 -0
  43. package/dist/plugins/devtools/review/personas/persona-session.d.ts +94 -0
  44. package/dist/plugins/devtools/review/personas/persona-session.js +1 -0
  45. package/dist/plugins/devtools/review/session/SessionPanel.svelte +11 -11
  46. package/dist/plugins/devtools/review/variations/VariationsPanel.svelte +12 -13
  47. package/dist/plugins/devtools/screens/custom-tools/CustomToolsScreen.svelte +11 -25
  48. package/dist/plugins/devtools/sidebar/Sidebar.svelte +2 -33
  49. package/dist/plugins/devtools/sidebar/SidebarRow.svelte +5 -42
  50. package/dist/plugins/devtools/sidebar/nav.d.ts +26 -28
  51. package/dist/plugins/devtools/sidebar/nav.js +1 -1
  52. package/package.json +2 -2
  53. package/dist/plugins/devtools/screens/experience-instances/ExperienceInstancesListScreen.svelte +0 -10
  54. package/dist/plugins/devtools/screens/experiences/ExperienceDetailsScreen.svelte +0 -44
  55. package/dist/plugins/devtools/screens/experiences/ExperienceExperienceInstancesListScreen.svelte +0 -13
  56. package/dist/plugins/devtools/screens/experiences/ExperienceScreen.svelte +0 -32
  57. package/dist/plugins/devtools/screens/experiences/ExperienceTreeScreen.svelte +0 -500
  58. package/dist/plugins/devtools/screens/experiences/ExperiencesListScreen.svelte +0 -22
  59. package/dist/plugins/devtools/screens/features/FeaturesListScreen.svelte +0 -239
  60. package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +0 -286
  61. package/dist/plugins/devtools/screens/responses/ResponsesListScreen.svelte +0 -12
  62. package/dist/plugins/devtools/screens/responses/ResponsesTable.svelte +0 -88
@@ -19,18 +19,22 @@
19
19
  import SidebarFooter from './sidebar/SidebarFooter.svelte'
20
20
  import {
21
21
  CUSTOM_TOOLS_TAB,
22
- DEVELOPER_OPEN_STORAGE_KEY,
23
22
  TAB_STORAGE_KEY,
24
23
  buildNav,
25
- developerTabId,
26
- isInDeveloper,
27
24
  readStored,
28
25
  resolveTab,
26
+ screenTabId,
29
27
  toolTabId,
30
28
  writeStored,
31
29
  } from './sidebar/nav'
32
30
  import { readDevtoolsOptions } from './options'
33
- import { footerTools, readCustomTools, reviewTools } from './customTools'
31
+ import {
32
+ developerTools,
33
+ footerTools,
34
+ personasTools,
35
+ readCustomTools,
36
+ reviewTools,
37
+ } from './customTools'
34
38
  import { provideDevtoolsSession } from './session-access'
35
39
  import { setPostureContext } from './posture-context'
36
40
  import { createSessionData } from './session'
@@ -38,6 +42,7 @@
38
42
  TAB_INTROS,
39
43
  configSourceLabel,
40
44
  derivePosture,
45
+ applyEdits,
41
46
  guideFixes,
42
47
  guideSections,
43
48
  readSavedPersonas,
@@ -54,26 +59,31 @@
54
59
  VariationsPanel,
55
60
  } from './review'
56
61
  import {
62
+ openPersonaJourney,
57
63
  screenQuestions,
58
64
  takePendingLoad,
59
65
  walkNotice,
60
66
  walkPersona,
67
+ type PendingLoad,
61
68
  type WalkSettings,
62
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'
63
77
  import ProjectScreen from './screens/project/ProjectScreen.svelte'
64
78
  import ComponentsListScreen from './screens/components/ComponentsListScreen.svelte'
65
79
  import ConfigScreen from './screens/config/ConfigScreen.svelte'
66
80
  import ContentListScreen from './screens/content/ContentListScreen.svelte'
67
81
  import EventsListScreen from './screens/events/EventsListScreen.svelte'
68
- import ExperienceInstancesListScreen from './screens/experience-instances/ExperienceInstancesListScreen.svelte'
69
- import ExperiencesListScreen from './screens/experiences/ExperiencesListScreen.svelte'
70
- import FeaturesListScreen from './screens/features/FeaturesListScreen.svelte'
71
82
  import FieldsListScreen from './screens/fields/FieldsListScreen.svelte'
72
83
  import FrameworksListScreen from './screens/frameworks/FrameworksListScreen.svelte'
73
84
  import HistoryEntriesListScreen from './screens/history/HistoryEntriesListScreen.svelte'
74
85
  import IdentityScreen from './screens/identity/IdentityScreen.svelte'
75
86
  import PredicatesListScreen from './screens/predicates/PredicatesListScreen.svelte'
76
- import ResponsesListScreen from './screens/responses/ResponsesListScreen.svelte'
77
87
  import CustomToolsScreen from './screens/custom-tools/CustomToolsScreen.svelte'
78
88
 
79
89
  const experience = getExperience()
@@ -83,6 +93,8 @@
83
93
  const registeredTools = readCustomTools(client)
84
94
  const tools = reviewTools(registeredTools)
85
95
  const footer = footerTools(registeredTools)
96
+ const personaTools = personasTools(registeredTools)
97
+ const customTools = developerTools(registeredTools)
86
98
  const posture = derivePosture({
87
99
  readOnly: options.readOnly,
88
100
  environmentLabel: options.environmentLabel,
@@ -108,22 +120,18 @@
108
120
  const sessionStorageOf = (): Storage => window.sessionStorage
109
121
  const localStorageOf = (): Storage => window.localStorage
110
122
 
111
- /** The developer screens, keyed by their sidebar id. */
112
- const DEVELOPER_COMPONENTS: Record<string, Component> = {
113
- [developerTabId('project')]: ProjectScreen,
114
- [developerTabId('components')]: ComponentsListScreen,
115
- [developerTabId('config')]: ConfigScreen,
116
- [developerTabId('content')]: ContentListScreen,
117
- [developerTabId('events')]: EventsListScreen,
118
- [developerTabId('experience-instances')]: ExperienceInstancesListScreen,
119
- [developerTabId('experiences')]: ExperiencesListScreen,
120
- [developerTabId('features')]: FeaturesListScreen,
121
- [developerTabId('fields')]: FieldsListScreen,
122
- [developerTabId('frameworks')]: FrameworksListScreen,
123
- [developerTabId('history')]: HistoryEntriesListScreen,
124
- [developerTabId('identity')]: IdentityScreen,
125
- [developerTabId('predicates')]: PredicatesListScreen,
126
- [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,
127
135
  }
128
136
 
129
137
  // ── The session record ───────────────────────────────────────────────────────────────────────
@@ -131,7 +139,7 @@
131
139
  // and the answer history listen from the start.
132
140
  const session = createSessionData(client, {
133
141
  exclude: options.hiddenExperienceKeys,
134
- precedence: options.journeyPrecedence,
142
+ precedence: options.experiencePrecedence,
135
143
  codeOwned: options.codeOwnedFields,
136
144
  })
137
145
  const releaseSession = provideDevtoolsSession(client, { session, posture })
@@ -153,27 +161,25 @@
153
161
  buildNav({
154
162
  variations: $features.length > 0,
155
163
  tools,
164
+ customTools,
156
165
  footerTools: footer,
157
166
  }),
158
167
  )
159
168
  const storedTab = readStored(sessionStorageOf, TAB_STORAGE_KEY)
160
169
  let tab = $state(storedTab ?? 'guide')
161
- let developerOpen = $state(readStored(sessionStorageOf, DEVELOPER_OPEN_STORAGE_KEY) === 'true')
162
170
  let drawerOpen = $state(false)
163
171
 
164
- // A tab can disappear under the viewer (Variations once the feature list is empty), and a stored
165
- // developer screen opens its group.
172
+ // A tab can disappear under the viewer: Feature flags once the feature list is empty.
166
173
  $effect(() => {
167
174
  const resolved = resolveTab(tab, nav)
168
175
  if (resolved !== tab) tab = resolved
169
- if (isInDeveloper(tab, nav) && !developerOpen) developerOpen = true
170
176
  })
171
177
 
172
- const selectedItem = $derived(
173
- [...nav.primary, ...nav.developer, ...nav.footer].find((item) => item.id === tab),
178
+ const selectedItem = $derived([...nav.primary, ...nav.footer].find((item) => item.id === tab))
179
+ const selectedTool = $derived(
180
+ [...tools, ...customTools, ...footer].find((tool) => toolTabId(tool.id) === tab),
174
181
  )
175
- const selectedTool = $derived([...tools, ...footer].find((tool) => toolTabId(tool.id) === tab))
176
- const developerScreen = $derived(DEVELOPER_COMPONENTS[tab])
182
+ const sdkScreen = $derived(SCREEN_COMPONENTS[tab])
177
183
 
178
184
  /**
179
185
  * Narrows a tab id to one of the review tabs.
@@ -198,16 +204,6 @@
198
204
  writeStored(sessionStorageOf, TAB_STORAGE_KEY, id)
199
205
  }
200
206
 
201
- /**
202
- * Opens or folds the Developer group, and remembers it.
203
- *
204
- * @example toggleDeveloper()
205
- */
206
- function toggleDeveloper(): void {
207
- developerOpen = !developerOpen
208
- writeStored(sessionStorageOf, DEVELOPER_OPEN_STORAGE_KEY, String(developerOpen))
209
- }
210
-
211
207
  // ── How to use ───────────────────────────────────────────────────────────────────────────────
212
208
  const guide = $derived.by(() => {
213
209
  const available = { variations: $features.length > 0 }
@@ -215,7 +211,12 @@
215
211
  ...section,
216
212
  tab: null,
217
213
  }))
218
- const toolSections: GuideSection[] = [...tools, ...footer].flatMap((tool) =>
214
+ const toolSections: GuideSection[] = [
215
+ ...tools.map((tool) => ({ tool, tab: toolTabId(tool.id) })),
216
+ ...personaTools.map((tool) => ({ tool, tab: 'personas' })),
217
+ ...customTools.map((tool) => ({ tool, tab: toolTabId(tool.id) })),
218
+ ...footer.map((tool) => ({ tool, tab: toolTabId(tool.id) })),
219
+ ].flatMap(({ tool, tab: toolTab }) =>
219
220
  tool.guide
220
221
  ? [
221
222
  {
@@ -223,7 +224,7 @@
223
224
  title: tool.label,
224
225
  steps: tool.guide.steps,
225
226
  notes: tool.guide.notes ?? [],
226
- tab: toolTabId(tool.id),
227
+ tab: toolTab,
227
228
  },
228
229
  ]
229
230
  : [],
@@ -237,8 +238,8 @@
237
238
  }
238
239
  })
239
240
 
240
- const followUp = options.answerFollowUp
241
- ? { label: options.answerFollowUp.label, run: () => options.answerFollowUp?.run(client) }
241
+ const followUp = options.responseFollowUp
242
+ ? { label: options.responseFollowUp.label, run: () => options.responseFollowUp?.run(client) }
242
243
  : null
243
244
 
244
245
  // ── The floating button ──────────────────────────────────────────────────────────────────────
@@ -255,49 +256,135 @@
255
256
  writeAutoHide(localStorageOf, value)
256
257
  }
257
258
 
258
- // ── A persona load, carried across its reload ────────────────────────────────────────────────
259
- // Load reloads the page clean, so the walk runs here, as the devtools mount on the new page. The
260
- // panel stays shut while it walks, so the reviewer watches the journey move, and opens on the
261
- // Personas tab when the walk stops short.
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.
262
266
  let personaWalkNotice = $state<{
263
267
  status: 'info' | 'success' | 'warning' | 'error'
264
268
  text: string
265
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
266
275
 
267
276
  const walkSettings: WalkSettings = {
268
277
  exclude: options.hiddenExperienceKeys,
269
- precedence: options.journeyPrecedence,
278
+ precedence: options.experiencePrecedence,
270
279
  openOrder: () => session.openOrderStore.getState(),
271
280
  selfAdvancing: options.personaWalk.selfAdvancingSteps,
272
- answers: options.personaWalk.answers,
281
+ responses: options.personaWalk.responses,
273
282
  askedOn: (fieldKeys) => screenQuestions(client, fieldKeys, options.codeOwnedFields),
274
283
  }
275
284
 
276
285
  /**
277
- * The persona a reload was for, among the site's and this browser's.
286
+ * A persona by id, among the site's as this browser edited them and this browser's own.
278
287
  *
279
- * @returns {Persona | undefined} The persona, or `undefined` when no load is waiting.
280
- * @example pendingPersona()
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)
281
292
  */
282
- function pendingPersona(): Persona | undefined {
283
- const id = takePendingLoad(sessionStorageOf())
284
- if (id === null || !posture.canEditAnswers) return undefined
285
- const saved = readSavedPersonas(localStorageOf, client.projectId)
286
- return [...options.personas, ...saved].find((persona) => persona.id === id)
287
- }
288
-
289
- const walking = pendingPersona()
290
- if (walking !== undefined) {
291
- personaWalkNotice = { status: 'info', text: `Loading ${walking.name}…` }
292
- void walkPersona(client, walking, walkSettings).then((outcome) => {
293
- const notice = walkNotice(walking, outcome)
294
- personaWalkNotice = notice
295
- if (notice.status === 'success') return
296
- selectTab('personas')
297
- experience.open()
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.
300
+ *
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)
305
+ */
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)
298
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
+ }
299
362
  }
300
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
+
301
388
  // ── Dialog behaviour ─────────────────────────────────────────────────────────────────────────
302
389
  /** The open panel; Svelte sets it back to `null` when the panel unmounts. */
303
390
  let panel: HTMLDivElement | null = $state(null)
@@ -423,13 +510,7 @@
423
510
  <div class="drawer-scrim" aria-hidden="true" onclick={() => (drawerOpen = false)}></div>
424
511
  {/if}
425
512
  <div class="nav-column" class:drawer-open={drawerOpen} id="devtools-nav">
426
- <Sidebar
427
- {nav}
428
- selected={tab}
429
- {developerOpen}
430
- onSelect={selectTab}
431
- onToggleDeveloper={toggleDeveloper}
432
- />
513
+ <Sidebar {nav} selected={tab} onSelect={selectTab} />
433
514
  <SidebarFooter
434
515
  tools={nav.footer}
435
516
  selected={tab}
@@ -440,9 +521,9 @@
440
521
  </div>
441
522
 
442
523
  <section class="pane" aria-label={selectedItem?.label} data-testid="devtools-pane">
443
- {#if developerScreen}
444
- {@const Screen = developerScreen}
445
- <div class="developer-body">
524
+ {#if sdkScreen}
525
+ {@const Screen = sdkScreen}
526
+ <div class="screen-body">
446
527
  <Screen />
447
528
  </div>
448
529
  {:else if tab === CUSTOM_TOOLS_TAB}
@@ -501,12 +582,15 @@
501
582
  {client}
502
583
  {posture}
503
584
  journey={$journey}
504
- personas={options.personas}
585
+ personas={sitePersonaList}
505
586
  asked={$asked}
506
587
  route={$route}
507
588
  hiddenKeys={options.hiddenExperienceKeys}
508
589
  codeOwned={options.codeOwnedFields}
509
590
  personaWalk={options.personaWalk}
591
+ tools={personaTools}
592
+ {activePersona}
593
+ onStop={stopPersonaSession}
510
594
  walkNotice={personaWalkNotice}
511
595
  onWalkNoticeDismiss={() => (personaWalkNotice = null)}
512
596
  />
@@ -524,6 +608,7 @@
524
608
  position={options.launcher.position}
525
609
  {autoHide}
526
610
  {preview}
611
+ walkingAs={activePersona?.name ?? null}
527
612
  onOpen={() => experience.open()}
528
613
  />
529
614
  {/if}
@@ -791,7 +876,7 @@
791
876
  display: grid;
792
877
  }
793
878
 
794
- .developer-body {
879
+ .screen-body {
795
880
  flex: 1 1 auto;
796
881
  display: grid;
797
882
  min-height: 0;
@@ -7,6 +7,8 @@
7
7
  import { Banner } from '../ui'
8
8
 
9
9
  export let tool: CustomTool
10
+ /** Drops the host's own padding, for a tool set inside another panel's section. */
11
+ export let flush = false
10
12
 
11
13
  const client = getPicked()
12
14
  const experience = getExperience()
@@ -73,16 +75,16 @@
73
75
  </script>
74
76
 
75
77
  {#if framework}
76
- <div class="adapted-tool-host" bind:this={host} hidden={renderFailed}></div>
78
+ <div class="adapted-tool-host" class:flush bind:this={host} hidden={renderFailed}></div>
77
79
  {#if renderFailed}
78
- <div class="adapted-tool-fallback">
80
+ <div class="adapted-tool-fallback" class:flush>
79
81
  <Banner status="error" title="Tool failed to render">
80
82
  The "{tool.label}" tool threw while rendering. See the browser console for details.
81
83
  </Banner>
82
84
  </div>
83
85
  {/if}
84
86
  {:else}
85
- <div class="adapted-tool-fallback">
87
+ <div class="adapted-tool-fallback" class:flush>
86
88
  <Banner status="warning" title="Framework not registered">
87
89
  This tool renders a "{tool.component.frameworkName}" component, but no matching framework is
88
90
  registered with the Picked client. Add the corresponding framework plugin to use it.
@@ -100,4 +102,8 @@
100
102
  .adapted-tool-fallback {
101
103
  padding: var(--space-4);
102
104
  }
105
+
106
+ .flush {
107
+ padding: 0;
108
+ }
103
109
  </style>
@@ -1,7 +1,7 @@
1
1
  /** @file Registry helpers for devtools custom tools — user-supplied, framework-adapted tools. */
2
2
  import type { AdaptedComponent, Picked } from '@picked/client';
3
3
  /** Where a custom tool is listed in the devtools sidebar. */
4
- export type CustomToolPlacement = 'review' | 'footer' | 'developer';
4
+ export type CustomToolPlacement = 'review' | 'personas' | 'footer' | 'developer';
5
5
  /** The glyphs a `'footer'` tool can show. */
6
6
  export declare const CUSTOM_TOOL_ICONS: readonly ['bug', 'wrench'];
7
7
  /** A glyph a `'footer'` tool can show. */
@@ -25,9 +25,10 @@ export interface CustomTool {
25
25
  label: string;
26
26
  component: AdaptedComponent;
27
27
  /**
28
- * `'review'` lists the tool as a tab of its own, beside the review tabs. `'footer'` shows it as
29
- * an icon at the foot of the sidebar, beside the auto-hide switch, with its label as a tooltip.
30
- * `'developer'` lists it in the Custom Tools tab, with the other unplaced tools.
28
+ * `'review'` lists the tool as a tab of its own, beside the review tabs. `'personas'` shows it
29
+ * as a section of the Personas & Scenarios tab, under its label. `'footer'` shows it as an icon
30
+ * at the foot of the sidebar, beside the auto-hide switch, with its label as a tooltip.
31
+ * `'developer'` lists it as a top-level row of its own, after the review-placed tools.
31
32
  *
32
33
  * @default 'developer'
33
34
  */
@@ -40,7 +41,7 @@ export interface CustomTool {
40
41
  icon?: CustomToolIcon;
41
42
  /** One sentence on what the tool is for, shown above it. */
42
43
  description?: string;
43
- /** How to use steps for the tool. Listed for a `'review'` or `'footer'` tool. */
44
+ /** How to use steps for the tool, listed under its label in How to use. */
44
45
  guide?: CustomToolGuide;
45
46
  }
46
47
  /**
@@ -64,6 +65,14 @@ export declare function readCustomTools(client: Pick<Picked, 'getPluginConfig'>)
64
65
  * @example reviewTools(readCustomTools(client))
65
66
  */
66
67
  export declare function reviewTools(tools: readonly CustomTool[]): CustomTool[];
68
+ /**
69
+ * The tools shown as sections of the Personas & Scenarios tab.
70
+ *
71
+ * @param {readonly CustomTool[]} tools - The registered tools.
72
+ * @returns {CustomTool[]} The tools placed `'personas'`, in registration order.
73
+ * @example personasTools(readCustomTools(client))
74
+ */
75
+ export declare function personasTools(tools: readonly CustomTool[]): CustomTool[];
67
76
  /**
68
77
  * The tools shown as icons at the foot of the sidebar.
69
78
  *
@@ -73,10 +82,10 @@ export declare function reviewTools(tools: readonly CustomTool[]): CustomTool[];
73
82
  */
74
83
  export declare function footerTools(tools: readonly CustomTool[]): CustomTool[];
75
84
  /**
76
- * The tools listed in the Custom Tools tab.
85
+ * The tools listed as top-level rows after the review-placed tools.
77
86
  *
78
87
  * @param {readonly CustomTool[]} tools - The registered tools.
79
- * @returns {CustomTool[]} Every tool placed neither `'review'` nor `'footer'`, in registration order.
88
+ * @returns {CustomTool[]} The unplaced tools and those placed `'developer'`, in registration order.
80
89
  * @example developerTools(readCustomTools(client))
81
90
  */
82
91
  export declare function developerTools(tools: readonly CustomTool[]): CustomTool[];
@@ -1 +1 @@
1
- import{DEVTOOLS_PLUGIN_NAME as e}from"./constants.js";var t=[`bug`,`wrench`];function n(e){return typeof e==`object`&&!!e}function r(e){return n(e)&&`frameworkName`in e&&typeof e.frameworkName==`string`&&`component`in e}function i(e){return t.some(t=>t===e)}function a(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function o(e){if(n(e)&&a(e.steps))return a(e.notes)?{steps:e.steps,notes:e.notes}:{steps:e.steps}}function s(e){if(!n(e))return null;let{id:t,label:a,component:s,placement:c,icon:l,description:u,guide:d}=e;if(typeof t!=`string`||typeof a!=`string`||!r(s))return null;let f={id:t,label:a,component:s};(c===`review`||c===`footer`||c===`developer`)&&(f.placement=c),i(l)&&(f.icon=l),typeof u==`string`&&(f.description=u);let p=o(d);return p&&(f.guide=p),f}function c(t){let n=t.getPluginConfig(e).customTools;if(!Array.isArray(n))return[];let r=new Set;return n.map(s).filter(e=>e!==null).filter(e=>!r.has(e.id)&&(r.add(e.id),!0))}function l(e){return e.filter(e=>e.placement===`review`)}function u(e){return e.filter(e=>e.placement===`footer`)}function d(e){return e.filter(e=>e.placement===void 0||e.placement===`developer`)}export{t as CUSTOM_TOOL_ICONS,d as developerTools,u as footerTools,c as readCustomTools,l as reviewTools};
1
+ import{DEVTOOLS_PLUGIN_NAME as e}from"./constants.js";var t=[`bug`,`wrench`];function n(e){return typeof e==`object`&&!!e}function r(e){return n(e)&&`frameworkName`in e&&typeof e.frameworkName==`string`&&`component`in e}function i(e){return t.some(t=>t===e)}var a=[`review`,`personas`,`footer`,`developer`];function o(e){return a.some(t=>t===e)}function s(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function c(e){if(n(e)&&s(e.steps))return s(e.notes)?{steps:e.steps,notes:e.notes}:{steps:e.steps}}function l(e){if(!n(e))return null;let{id:t,label:a,component:s,placement:l,icon:u,description:d,guide:f}=e;if(typeof t!=`string`||typeof a!=`string`||!r(s))return null;let p={id:t,label:a,component:s};o(l)&&(p.placement=l),i(u)&&(p.icon=u),typeof d==`string`&&(p.description=d);let m=c(f);return m&&(p.guide=m),p}function u(t){let n=t.getPluginConfig(e).customTools;if(!Array.isArray(n))return[];let r=new Set;return n.map(l).filter(e=>e!==null).filter(e=>!r.has(e.id)&&(r.add(e.id),!0))}function d(e){return e.filter(e=>e.placement===`review`)}function f(e){return e.filter(e=>e.placement===`personas`)}function p(e){return e.filter(e=>e.placement===`footer`)}function m(e){return e.filter(e=>e.placement===void 0||e.placement===`developer`)}export{t as CUSTOM_TOOL_ICONS,m as developerTools,p as footerTools,f as personasTools,u as readCustomTools,d as reviewTools};
@@ -12,7 +12,7 @@ export interface AnswerField {
12
12
  label: string;
13
13
  }[] | null;
14
14
  }
15
- /** Everything a row of the Answers panel needs about one answer. */
15
+ /** Everything a row of the Responses tab needs about one response. */
16
16
  export interface AnswerView {
17
17
  /** The question as the customer met it. */
18
18
  title: string;
@@ -1 +1 @@
1
- import{deslug as e}from"../plain-language/plain-language.js";var t=`Not answered yet`,n=`Answered on its screen`;function r(t){let n=t.name?.trim();return n!==void 0&&n.length>0?n:e(t.key)}function i(e){let t=e.label?.trim();return t!==void 0&&t.length>0?t:r(e)}function a(e){return e.type===`object`?`system`:e.options!==null&&e.options!==void 0&&e.options.length>0?`options`:e.type===`boolean`?`boolean`:e.type===`number`?`number`:`text`}function o(e,n){let r=n.options?.find(t=>t.value===e);return r===void 0?e===!0?`Yes`:e===!1?`No`:e===null?t:typeof e==`string`?e:typeof e==`number`?String(e):JSON.stringify(e):r.label}function s(e,t){return e==null||e===``?null:Array.isArray(e)?e.length===0?null:e.map(e=>o(e,t)).join(`, `):a(t)===`system`?n:o(e,t)}function c(e,t){let n=a(t),o=s(e,t);return{title:i(t),noun:r(t),kind:n,display:o,answered:o!==null,editable:n!==`system`}}function l(e,t,n=new Set){let r=new Set;for(let e of t)for(let t of e)t.type===`step`&&t.stepId!==void 0&&r.add(t.stepId);let i=new Set;for(let t of e)if(r.has(t.id))for(let e of t.fieldKeys)n.has(e)||i.add(e);return i}function u(e){return e===void 0?``:JSON.stringify(e,null,2)}export{l as askedFieldKeys,c as describeAnswer,a as kindOf,r as nounOf,i as questionOf,s as renderAnswer,u as systemPayload};
1
+ import{deslug as e}from"../plain-language/plain-language.js";var t=`No response yet`,n=`Set on its step`;function r(t){let n=t.name?.trim();return n!==void 0&&n.length>0?n:e(t.key)}function i(e){let t=e.label?.trim();return t!==void 0&&t.length>0?t:r(e)}function a(e){return e.type===`object`?`system`:e.options!==null&&e.options!==void 0&&e.options.length>0?`options`:e.type===`boolean`?`boolean`:e.type===`number`?`number`:`text`}function o(e,n){let r=n.options?.find(t=>t.value===e);return r===void 0?e===!0?`Yes`:e===!1?`No`:e===null?t:typeof e==`string`?e:typeof e==`number`?String(e):JSON.stringify(e):r.label}function s(e,t){return e==null||e===``?null:Array.isArray(e)?e.length===0?null:e.map(e=>o(e,t)).join(`, `):a(t)===`system`?n:o(e,t)}function c(e,t){let n=a(t),o=s(e,t);return{title:i(t),noun:r(t),kind:n,display:o,answered:o!==null,editable:n!==`system`}}function l(e,t,n=new Set){let r=new Set;for(let e of t)for(let t of e)t.type===`step`&&t.stepId!==void 0&&r.add(t.stepId);let i=new Set;for(let t of e)if(r.has(t.id))for(let e of t.fieldKeys)n.has(e)||i.add(e);return i}function u(e){return e===void 0?``:JSON.stringify(e,null,2)}export{l as askedFieldKeys,c as describeAnswer,a as kindOf,r as nounOf,i as questionOf,s as renderAnswer,u as systemPayload};
@@ -1 +1 @@
1
- var e=[{id:`session`,title:`See where you are`,steps:[],notes:[`To see the screen you are on and every answer so far, open **This session**.`],tab:`session`},{id:`answers`,title:`Change an answer`,steps:[`Open **Answers**.`,`Find the question. Use **Answered**, **Not answered** or **All** to narrow the list.`,`Click **Change**, or **Answer** if it has none yet.`,`Pick or type the new answer, then click **Save**.`],notes:[`To remove one answer, click **Clear** beside it.`,`To start the whole journey again, click **Start over**, then **Clear everything**.`,`If a question says **Change it on its screen**, go back to that screen and change it there.`],tab:`answers`},{id:`journey`,title:`Go back to an earlier screen`,steps:[`Open **Journey map**.`,`Find a screen on your route. Your route is blue, and your screen says **You are here**.`,`Click the screen, then click **Go back to this screen**.`],notes:[`You can only go back to screens you have already visited.`,`To change how the map looks, pick **Map**, **Timeline** or **Outline** at the top.`],tab:`journey`},{id:`personas`,title:`Try the journey as a customer`,steps:[`Open **Personas & Scenarios**.`,`Click **Load** beside the customer you want. The journey opens where they would be.`],notes:[`To keep where you are for later, click **Create**, give it a name, then click **Save**.`,`What you save stays in this browser only.`],tab:`personas`},{id:`variations`,title:`View a test version of a page`,steps:[`Open **Variations**.`,`Find the test and pick the version you want.`,`Click **Show me this version**. 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 test, click **Leave this one alone**.`],tab:`variations`}],t=[{tab:`journey`,entry:{problem:`I can't click a screen on the Journey map`,fix:`Go back only to screens 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};
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 { DEVELOPER_LABEL, NODE_LABELS, TAB_INTROS, TAB_LABELS, configSourceLabel, deslug, screenName, } from './plain-language';
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, PersonaLanding, PersonaLoader, PersonaScreen, PersonaSource, PersonaStorageGetter, } from './personas';
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{DEVELOPER_LABEL as r,NODE_LABELS as i,TAB_INTROS as a,TAB_LABELS as o,configSourceLabel as s,deslug as c,screenName as l}from"./plain-language/plain-language.js";import{PERSONA_STORAGE_PREFIX as u,buildPersona as d,customerAnswers as f,describeLanding as p,entryUrlOf as m,passedScreens as h,personaStorageKey as g,readAnswers as _,readEntryUrl as v,readSavedPersonas as y,sitePersonas as b,writeSavedPersonas as x}from"./personas/personas.js";import{askedFieldKeys as S,describeAnswer as C,kindOf as w,nounOf as T,questionOf as E,renderAnswer as D,systemPayload as O}from"./answer-display/answer-display.js";import{emphasis as k,guideFixes as A,guideSections as j}from"./guide-sections/guide-sections.js";import{UNDESCRIBABLE as M,describeRule as N,varKeyOf as P}from"./plain-logic/plain-logic.js";import{buildOutline as F}from"./journey-outline/journey-outline.js";import{nextOpenOrder as I,pickJourney as L}from"./journey-pick/journey-pick.js";import{BREADTH as R,DEPTH as z,buildTree as B}from"./journey-tree/journey-tree.js";import"./journey-tree/index.js";import{COLOURS as V,LANE_WIDTH as H,ROW_HEIGHT as U,buildTimeline as W}from"./journey-timeline/journey-timeline.js";import"./journey-timeline/index.js";import{builderGraph as G,responseKeysOf as K,walkDraft as q}from"./persona-builder/persona-builder.js";import{derivePosture as J}from"./safety-posture/safety-posture.js";import{nextTrails as Y}from"./visited-steps/visited-steps.js";import{nextRoute as X}from"./walk-route/walk-route.js";export{R as BREADTH,V as COLOURS,z as DEPTH,r as DEVELOPER_LABEL,H as LANE_WIDTH,i as NODE_LABELS,u as PERSONA_STORAGE_PREFIX,U as ROW_HEIGHT,a as TAB_INTROS,o as TAB_LABELS,M as UNDESCRIBABLE,S as askedFieldKeys,F as buildOutline,d as buildPersona,W as buildTimeline,B as buildTree,G as builderGraph,s as configSourceLabel,f as customerAnswers,J as derivePosture,C as describeAnswer,p as describeLanding,N as describeRule,c as deslug,k as emphasis,m as entryUrlOf,A as guideFixes,j as guideSections,e as isBlank,w as kindOf,t as nextHistory,I as nextOpenOrder,X as nextRoute,Y as nextTrails,T as nounOf,h as passedScreens,g as personaStorageKey,L as pickJourney,E as questionOf,_ as readAnswers,v as readEntryUrl,y as readSavedPersonas,D as renderAnswer,K as responseKeysOf,l as screenName,b as sitePersonas,n as submittedAnswers,O as systemPayload,P as varKeyOf,q as walkDraft,x as writeSavedPersonas};
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 +1 @@
1
- import{NODE_LABELS as e,deslug as t,screenName as n}from"../plain-language/plain-language.js";import{describeRule as r}from"../plain-logic/plain-logic.js";var i=[{handle:`true`,label:`Yes`},{handle:`false`,label:`Otherwise`}],a=400,o=24,s=new Set([`origin`,`step`,`condition`,`experience`]);function c(e,t,n){return e!==null&&e===n?`current`:e!==null&&t.has(e)?`visited`:`ahead`}function l({nodes:l,edges:u,conditions:d,fieldLookup:f,visited:p,currentStepKey:m}){let h=new Map(l.map(e=>[e.id,e])),g=new Map(d.map(e=>[e.id,e])),_=new Map;for(let e of u){let t=_.get(e.sourceNodeId);t===void 0?_.set(e.sourceNodeId,[e]):t.push(e)}let v=[],y=new Map,b=(e,t)=>(_.get(e)??[]).find(e=>e.sourceHandle===t),x=e=>{let t=_.get(e)??[];return t.find(e=>e.sourceHandle===`true`)??t.find(e=>e.sourceHandle===void 0)??t[0]},S=e=>!s.has(e.type),C=(r,i)=>{if(r.type===`origin`)return{row:{kind:`start`,id:r.id,depth:i,label:e.origin},backref:e.origin};if(r.type===`step`){let e=n(r.name,r.stepKey);return{row:{kind:`screen`,id:r.id,depth:i,label:e,stepKey:r.stepKey??null,state:c(r.stepKey??null,p,m)},backref:e}}if(r.type===`experience`){let e=r.experienceKey??`another journey`;return{row:{kind:`journey`,id:r.id,depth:i,label:t(e)},backref:`opening ${e}`}}let a=t(r.actionKey??r.name??r.type);return{row:{kind:`end`,id:r.id,depth:i,label:a},backref:a}},w=e=>{let t=new Set,n=e;for(;!t.has(n);){let e=h.get(n);if(e===void 0||!S(e))return{next:n};t.add(n);let r=x(n);if(r===void 0)return{end:e};n=r.destinationNodeId}return null},T=(e,t)=>{let n=e.conditionId===void 0?void 0:g.get(e.conditionId),a=r(n?.logic,f);y.set(e.id,`the “${a}” decision`),v.push({kind:`decision`,id:e.id,depth:t,label:a,rule:JSON.stringify(n?.logic??null,null,2)});for(let n of i){let r=b(e.id,n.handle);r!==void 0&&(v.push({kind:`arm`,id:`${e.id}:${n.handle}`,depth:t+1,label:n.label}),E(r.destinationNodeId,t+2))}},E=(e,t)=>{if(e===void 0||v.length>=a||t>o)return;let n=w(e);if(n===null)return;if(`end`in n){let e=C(n.end,t).row.label;v.push({kind:`end`,id:`${n.end.id}:end:${v.length}`,depth:t,label:e});return}let r=n.next,i=h.get(r);if(i===void 0)return;let s=y.get(r);if(s!==void 0){v.push({kind:`backref`,id:`${r}:back:${v.length}`,depth:t,label:s});return}if(i.type===`condition`){T(i,t);return}let{row:c,backref:l}=C(i,t);y.set(r,l),v.push(c),E(x(r)?.destinationNodeId,t)};if(E(l.find(e=>e.type===`origin`)?.id,0),v.length===0)for(let e of l)e.type===`step`&&v.push(C(e,0).row);return v}export{l as buildOutline};
1
+ import{NODE_LABELS as e,deslug as t,screenName as n}from"../plain-language/plain-language.js";import{describeRule as r}from"../plain-logic/plain-logic.js";var i=[{handle:`true`,label:`Yes`},{handle:`false`,label:`Otherwise`}],a=400,o=24,s=new Set([`origin`,`step`,`condition`,`experience`]);function c(e,t,n){return e!==null&&e===n?`current`:e!==null&&t.has(e)?`visited`:`ahead`}function l({nodes:l,edges:u,conditions:d,fieldLookup:f,visited:p,currentStepKey:m}){let h=new Map(l.map(e=>[e.id,e])),g=new Map(d.map(e=>[e.id,e])),_=new Map;for(let e of u){let t=_.get(e.sourceNodeId);t===void 0?_.set(e.sourceNodeId,[e]):t.push(e)}let v=[],y=new Map,b=(e,t)=>(_.get(e)??[]).find(e=>e.sourceHandle===t),x=e=>{let t=_.get(e)??[];return t.find(e=>e.sourceHandle===`true`)??t.find(e=>e.sourceHandle===void 0)??t[0]},S=e=>!s.has(e.type),C=(r,i)=>{if(r.type===`origin`)return{row:{kind:`start`,id:r.id,depth:i,label:e.origin},backref:e.origin};if(r.type===`step`){let e=n(r.name,r.stepKey);return{row:{kind:`screen`,id:r.id,depth:i,label:e,stepKey:r.stepKey??null,state:c(r.stepKey??null,p,m)},backref:e}}if(r.type===`experience`){let e=r.experienceKey??`another experience`;return{row:{kind:`journey`,id:r.id,depth:i,label:t(e)},backref:`opening ${e}`}}let a=t(r.actionKey??r.name??r.type);return{row:{kind:`end`,id:r.id,depth:i,label:a},backref:a}},w=e=>{let t=new Set,n=e;for(;!t.has(n);){let e=h.get(n);if(e===void 0||!S(e))return{next:n};t.add(n);let r=x(n);if(r===void 0)return{end:e};n=r.destinationNodeId}return null},T=(e,t)=>{let n=e.conditionId===void 0?void 0:g.get(e.conditionId),a=r(n?.logic,f);y.set(e.id,`the “${a}” condition`),v.push({kind:`decision`,id:e.id,depth:t,label:a,rule:JSON.stringify(n?.logic??null,null,2)});for(let n of i){let r=b(e.id,n.handle);r!==void 0&&(v.push({kind:`arm`,id:`${e.id}:${n.handle}`,depth:t+1,label:n.label}),E(r.destinationNodeId,t+2))}},E=(e,t)=>{if(e===void 0||v.length>=a||t>o)return;let n=w(e);if(n===null)return;if(`end`in n){let e=C(n.end,t).row.label;v.push({kind:`end`,id:`${n.end.id}:end:${v.length}`,depth:t,label:e});return}let r=n.next,i=h.get(r);if(i===void 0)return;let s=y.get(r);if(s!==void 0){v.push({kind:`backref`,id:`${r}:back:${v.length}`,depth:t,label:s});return}if(i.type===`condition`){T(i,t);return}let{row:c,backref:l}=C(i,t);y.set(r,l),v.push(c),E(x(r)?.destinationNodeId,t)};if(E(l.find(e=>e.type===`origin`)?.id,0),v.length===0)for(let e of l)e.type===`step`&&v.push(C(e,0).row);return v}export{l as buildOutline};