@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/plugins/devtools/DevToolsExperience.svelte +169 -84
- package/dist/plugins/devtools/components/AdaptedToolHost.svelte +9 -3
- package/dist/plugins/devtools/customTools.d.ts +16 -7
- package/dist/plugins/devtools/customTools.js +1 -1
- package/dist/plugins/devtools/derive/answer-display/answer-display.d.ts +1 -1
- package/dist/plugins/devtools/derive/answer-display/answer-display.js +1 -1
- 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/journey-outline/journey-outline.js +1 -1
- package/dist/plugins/devtools/derive/journey-tree/journey-tree.js +1 -1
- package/dist/plugins/devtools/derive/persona-builder/persona-builder.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 +89 -8
- 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 +20 -25
- package/dist/plugins/devtools/derive/plain-language/plain-language.js +1 -1
- package/dist/plugins/devtools/derive/plain-logic/plain-logic.d.ts +1 -1
- package/dist/plugins/devtools/derive/plain-logic/plain-logic.js +1 -1
- package/dist/plugins/devtools/derive/safety-posture/safety-posture.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 +27 -24
- package/dist/plugins/devtools/options.js +1 -1
- package/dist/plugins/devtools/review/answers/AnswersPanel.svelte +14 -14
- package/dist/plugins/devtools/review/guide/GuidePanel.svelte +1 -1
- package/dist/plugins/devtools/review/index.d.ts +2 -2
- package/dist/plugins/devtools/review/journey/ExperienceDetails.svelte +129 -0
- package/dist/plugins/devtools/review/journey/JourneyMap.svelte +16 -14
- package/dist/plugins/devtools/review/journey/JourneyPanel.svelte +70 -24
- package/dist/plugins/devtools/review/journey/JourneyTimeline.svelte +22 -21
- package/dist/plugins/devtools/review/personas/PersonaBuilder.svelte +15 -46
- package/dist/plugins/devtools/review/personas/PersonaCard.svelte +227 -44
- package/dist/plugins/devtools/review/personas/PersonasPanel.svelte +191 -78
- package/dist/plugins/devtools/review/personas/load-persona.d.ts +43 -22
- 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/review/session/SessionPanel.svelte +11 -11
- package/dist/plugins/devtools/review/variations/VariationsPanel.svelte +12 -13
- package/dist/plugins/devtools/screens/custom-tools/CustomToolsScreen.svelte +11 -25
- package/dist/plugins/devtools/sidebar/Sidebar.svelte +2 -33
- package/dist/plugins/devtools/sidebar/SidebarRow.svelte +5 -42
- package/dist/plugins/devtools/sidebar/nav.d.ts +26 -28
- 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
|
@@ -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 {
|
|
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
|
|
112
|
-
const
|
|
113
|
-
[
|
|
114
|
-
[
|
|
115
|
-
[
|
|
116
|
-
[
|
|
117
|
-
[
|
|
118
|
-
[
|
|
119
|
-
[
|
|
120
|
-
[
|
|
121
|
-
[
|
|
122
|
-
[
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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[] = [
|
|
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:
|
|
227
|
+
tab: toolTab,
|
|
227
228
|
},
|
|
228
229
|
]
|
|
229
230
|
: [],
|
|
@@ -237,8 +238,8 @@
|
|
|
237
238
|
}
|
|
238
239
|
})
|
|
239
240
|
|
|
240
|
-
const followUp = options.
|
|
241
|
-
? { label: options.
|
|
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
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
//
|
|
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.
|
|
278
|
+
precedence: options.experiencePrecedence,
|
|
270
279
|
openOrder: () => session.openOrderStore.getState(),
|
|
271
280
|
selfAdvancing: options.personaWalk.selfAdvancingSteps,
|
|
272
|
-
|
|
281
|
+
responses: options.personaWalk.responses,
|
|
273
282
|
askedOn: (fieldKeys) => screenQuestions(client, fieldKeys, options.codeOwnedFields),
|
|
274
283
|
}
|
|
275
284
|
|
|
276
285
|
/**
|
|
277
|
-
*
|
|
286
|
+
* A persona by id, among the site's as this browser edited them and this browser's own.
|
|
278
287
|
*
|
|
279
|
-
* @
|
|
280
|
-
* @
|
|
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
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
|
444
|
-
{@const Screen =
|
|
445
|
-
<div class="
|
|
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={
|
|
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
|
-
.
|
|
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. `'
|
|
29
|
-
*
|
|
30
|
-
*
|
|
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
|
|
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
|
|
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[]}
|
|
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
|
|
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
|
|
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=`
|
|
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
|
|
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 +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
|
|
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};
|