@picked/svelte 0.0.1-dev.1789541668638 → 0.0.1-dev.1793756239746
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/LICENSE +1 -0
- package/README.md +51 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/_virtual/_vite/preload-helper.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/experience/ExperienceHost.svelte +29 -13
- package/dist/experience/ExperienceSlot.svelte +21 -3
- package/dist/experience/ExperienceWrapper.svelte +23 -0
- package/dist/form/index.js +1 -1
- package/dist/hooks/action.d.ts +47 -18
- package/dist/hooks/action.js +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/step.d.ts +14 -0
- package/dist/hooks/step.js +1 -1
- package/dist/plugins/devtools/DevToolsExperience.svelte +545 -203
- package/dist/plugins/devtools/components/AdaptedToolHost.svelte +2 -2
- package/dist/plugins/devtools/constants.d.ts +5 -0
- package/dist/plugins/devtools/constants.js +1 -0
- package/dist/plugins/devtools/customTools.d.ts +42 -6
- package/dist/plugins/devtools/customTools.js +1 -1
- package/dist/plugins/devtools/derive/answer-display/answer-display.d.ts +112 -0
- package/dist/plugins/devtools/derive/answer-display/answer-display.js +1 -0
- package/dist/plugins/devtools/derive/answer-display/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/answer-display/index.js +1 -0
- package/dist/plugins/devtools/derive/answer-history/answer-history.d.ts +76 -0
- package/dist/plugins/devtools/derive/answer-history/answer-history.js +1 -0
- package/dist/plugins/devtools/derive/answer-history/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/answer-history/index.js +1 -0
- package/dist/plugins/devtools/derive/guide-sections/guide-sections.d.ts +67 -0
- package/dist/plugins/devtools/derive/guide-sections/guide-sections.js +1 -0
- package/dist/plugins/devtools/derive/guide-sections/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/guide-sections/index.js +1 -0
- package/dist/plugins/devtools/derive/index.d.ts +28 -0
- package/dist/plugins/devtools/derive/index.js +1 -0
- package/dist/plugins/devtools/derive/journey-outline/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/journey-outline/index.js +1 -0
- package/dist/plugins/devtools/derive/journey-outline/journey-outline.d.ts +86 -0
- package/dist/plugins/devtools/derive/journey-outline/journey-outline.js +1 -0
- package/dist/plugins/devtools/derive/journey-pick/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/journey-pick/index.js +1 -0
- package/dist/plugins/devtools/derive/journey-pick/journey-pick.d.ts +73 -0
- package/dist/plugins/devtools/derive/journey-pick/journey-pick.js +1 -0
- package/dist/plugins/devtools/derive/journey-timeline/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/journey-timeline/index.js +1 -0
- package/dist/plugins/devtools/derive/journey-timeline/journey-timeline.d.ts +70 -0
- package/dist/plugins/devtools/derive/journey-timeline/journey-timeline.js +1 -0
- package/dist/plugins/devtools/derive/journey-tree/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/journey-tree/index.js +1 -0
- package/dist/plugins/devtools/derive/journey-tree/journey-tree.d.ts +191 -0
- package/dist/plugins/devtools/derive/journey-tree/journey-tree.js +1 -0
- package/dist/plugins/devtools/derive/plain-language/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/plain-language/index.js +1 -0
- package/dist/plugins/devtools/derive/plain-language/plain-language.d.ts +73 -0
- package/dist/plugins/devtools/derive/plain-language/plain-language.js +1 -0
- package/dist/plugins/devtools/derive/plain-logic/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/plain-logic/index.js +1 -0
- package/dist/plugins/devtools/derive/plain-logic/plain-logic.d.ts +48 -0
- package/dist/plugins/devtools/derive/plain-logic/plain-logic.js +1 -0
- package/dist/plugins/devtools/derive/safety-posture/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/safety-posture/index.js +1 -0
- package/dist/plugins/devtools/derive/safety-posture/safety-posture.d.ts +43 -0
- package/dist/plugins/devtools/derive/safety-posture/safety-posture.js +1 -0
- package/dist/plugins/devtools/derive/visited-steps/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/visited-steps/index.js +1 -0
- package/dist/plugins/devtools/derive/visited-steps/visited-steps.d.ts +35 -0
- package/dist/plugins/devtools/derive/visited-steps/visited-steps.js +1 -0
- package/dist/plugins/devtools/derive/walk-route/index.d.ts +3 -0
- package/dist/plugins/devtools/derive/walk-route/index.js +1 -0
- package/dist/plugins/devtools/derive/walk-route/walk-route.d.ts +22 -0
- package/dist/plugins/devtools/derive/walk-route/walk-route.js +1 -0
- package/dist/plugins/devtools/index.d.ts +5 -2
- package/dist/plugins/devtools/index.js +1 -1
- package/dist/plugins/devtools/launcher/Launcher.svelte +150 -0
- package/dist/plugins/devtools/launcher/launcher.d.ts +36 -0
- package/dist/plugins/devtools/launcher/launcher.js +1 -0
- package/dist/plugins/devtools/options.d.ts +106 -0
- package/dist/plugins/devtools/options.js +1 -0
- package/dist/plugins/devtools/plugin.d.ts +8 -8
- package/dist/plugins/devtools/plugin.js +1 -1
- package/dist/plugins/devtools/posture-context.d.ts +15 -0
- package/dist/plugins/devtools/posture-context.js +1 -0
- package/dist/plugins/devtools/review/answers/AnswersPanel.svelte +526 -0
- package/dist/plugins/devtools/review/guide/GuideLine.svelte +20 -0
- package/dist/plugins/devtools/review/guide/GuidePanel.svelte +133 -0
- package/dist/plugins/devtools/review/index.d.ts +6 -0
- package/dist/plugins/devtools/review/index.js +1 -0
- package/dist/plugins/devtools/review/journey/JourneyMap.svelte +1078 -0
- package/dist/plugins/devtools/review/journey/JourneyPanel.svelte +608 -0
- package/dist/plugins/devtools/review/journey/JourneyTimeline.svelte +756 -0
- package/dist/plugins/devtools/review/journey/TimelineMarker.svelte +59 -0
- package/dist/plugins/devtools/review/session/SessionPanel.svelte +214 -0
- package/dist/plugins/devtools/review/variations/VariationsPanel.svelte +279 -0
- package/dist/plugins/devtools/screens/config/ConfigScreen.svelte +6 -0
- package/dist/plugins/devtools/screens/custom-tools/CustomToolsScreen.svelte +3 -3
- package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +6 -0
- package/dist/plugins/devtools/session/index.d.ts +3 -0
- package/dist/plugins/devtools/session/index.js +1 -0
- package/dist/plugins/devtools/session/session.d.ts +90 -0
- package/dist/plugins/devtools/session/session.js +1 -0
- package/dist/plugins/devtools/session-access.d.ts +32 -0
- package/dist/plugins/devtools/session-access.js +1 -0
- package/dist/plugins/devtools/sidebar/Sidebar.svelte +105 -0
- package/dist/plugins/devtools/sidebar/SidebarRow.svelte +117 -0
- package/dist/plugins/devtools/sidebar/nav.d.ts +105 -0
- package/dist/plugins/devtools/sidebar/nav.js +1 -0
- package/dist/plugins/devtools/ui/actions/Button.svelte +10 -1
- package/dist/plugins/devtools/ui/actions/CopyButton.svelte +13 -1
- package/dist/plugins/devtools/ui/data/Badge.svelte +3 -2
- package/dist/plugins/devtools/ui/data/EmptyState.svelte +16 -1
- package/dist/plugins/devtools/ui/feedback/Banner.svelte +3 -2
- package/dist/plugins/framework/index.js +1 -1
- package/dist/render/browser.js +1 -1
- package/package.json +23 -12
- package/dist/form/__tests__/CreateFormHarness.svelte +0 -30
- package/dist/hooks/events.d.ts +0 -2
- package/dist/hooks/events.js +0 -1
- package/dist/vite-env.d.js +0 -0
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
/**
|
|
23
23
|
* Records a tool render failure: surfaces a banner and logs to the console. A consumer tool's
|
|
24
24
|
* render failure is deliberately not routed through the client event bus, so a third-party tool
|
|
25
|
-
* bug never reaches the platform's
|
|
25
|
+
* bug never reaches the platform's exception telemetry.
|
|
26
26
|
*
|
|
27
27
|
* @param {unknown} error - The error thrown or rejected by the framework renderer.
|
|
28
28
|
* @example
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
function handleRenderError(error: unknown) {
|
|
32
32
|
renderFailed = true
|
|
33
|
-
// eslint-disable-next-line no-console -- surface tool failures locally; consumer errors must not reach
|
|
33
|
+
// eslint-disable-next-line no-console -- surface tool failures locally; consumer errors must not reach platform telemetry
|
|
34
34
|
console.error(`Custom devtools tool "${tool.label}" failed to render`, error)
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** @file Names the devtools plugin and its experience are registered under. */
|
|
2
|
+
/** Plugin name under which the devtools plugin stores its config (including registered custom tools). */
|
|
3
|
+
export declare const DEVTOOLS_PLUGIN_NAME = "svelte-devtools";
|
|
4
|
+
/** Key of the code-defined devtools experience; the journey views never list it. */
|
|
5
|
+
export declare const DEVTOOLS_EXPERIENCE_KEY = "devtools";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=`svelte-devtools`,t=`devtools`;export{t as DEVTOOLS_EXPERIENCE_KEY,e as DEVTOOLS_PLUGIN_NAME};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
/** @file Registry helpers for
|
|
1
|
+
/** @file Registry helpers for devtools custom tools — user-supplied, framework-adapted tools. */
|
|
2
2
|
import type { AdaptedComponent, Picked } from '@picked/client';
|
|
3
|
+
/** Where a custom tool is listed in the devtools sidebar. */
|
|
4
|
+
export type CustomToolPlacement = 'review' | 'developer';
|
|
5
|
+
/** How to use steps for a custom tool, listed under the tool's label. */
|
|
6
|
+
export interface CustomToolGuide {
|
|
7
|
+
/** Numbered steps, in order. `**text**` marks a control's label. */
|
|
8
|
+
steps: readonly string[];
|
|
9
|
+
/** Short lines after the steps. */
|
|
10
|
+
notes?: readonly string[];
|
|
11
|
+
}
|
|
3
12
|
/**
|
|
4
|
-
* A user-defined
|
|
13
|
+
* A user-defined tool surfaced in the devtools sidebar.
|
|
5
14
|
*
|
|
6
15
|
* The component is framework-adapted (via `adaptReactComponent` or `adaptSvelteComponent`), so
|
|
7
16
|
* tools can be authored in the host app's framework and still receive the live Picked client the
|
|
@@ -11,17 +20,44 @@ export interface CustomTool {
|
|
|
11
20
|
id: string;
|
|
12
21
|
label: string;
|
|
13
22
|
component: AdaptedComponent;
|
|
23
|
+
/**
|
|
24
|
+
* `'review'` lists the tool as a tab of its own, beside the review tabs. `'developer'` lists it
|
|
25
|
+
* under the Developer group's Custom Tools screen.
|
|
26
|
+
*
|
|
27
|
+
* @default 'developer'
|
|
28
|
+
*/
|
|
29
|
+
placement?: CustomToolPlacement;
|
|
30
|
+
/** One sentence on what the tool is for, shown above it. */
|
|
31
|
+
description?: string;
|
|
32
|
+
/** How to use steps for the tool. Listed only for a `'review'` tool. */
|
|
33
|
+
guide?: CustomToolGuide;
|
|
14
34
|
}
|
|
15
35
|
/**
|
|
16
36
|
* Reads the custom tools registered with the devtools plugin for a given client.
|
|
17
37
|
*
|
|
18
38
|
* Malformed entries are dropped, and entries that repeat an already-seen `id` are dropped
|
|
19
|
-
* (first registration wins), so a single bad or duplicate registration can't break the
|
|
20
|
-
* `id` is used as a keyed-each key when the tools are rendered
|
|
39
|
+
* (first registration wins), so a single bad or duplicate registration can't break the sidebar —
|
|
40
|
+
* the `id` is used as a keyed-each key when the tools are rendered.
|
|
21
41
|
*
|
|
22
|
-
* @param {Picked} client - The
|
|
42
|
+
* @param {Pick<Picked, 'getPluginConfig'>} client - The client whose plugin config holds the registered tools.
|
|
23
43
|
* @returns {CustomTool[]} The valid, id-unique registered custom tools, or an empty array when none exist.
|
|
24
44
|
* @example
|
|
25
45
|
* const tools = readCustomTools(getPicked())
|
|
26
46
|
*/
|
|
27
|
-
export declare function readCustomTools(client: Picked): CustomTool[];
|
|
47
|
+
export declare function readCustomTools(client: Pick<Picked, 'getPluginConfig'>): CustomTool[];
|
|
48
|
+
/**
|
|
49
|
+
* The tools listed as tabs of their own, beside the review tabs.
|
|
50
|
+
*
|
|
51
|
+
* @param {readonly CustomTool[]} tools - The registered tools.
|
|
52
|
+
* @returns {CustomTool[]} The tools placed `'review'`, in registration order.
|
|
53
|
+
* @example reviewTools(readCustomTools(client))
|
|
54
|
+
*/
|
|
55
|
+
export declare function reviewTools(tools: readonly CustomTool[]): CustomTool[];
|
|
56
|
+
/**
|
|
57
|
+
* The tools listed under the Developer group's Custom Tools screen.
|
|
58
|
+
*
|
|
59
|
+
* @param {readonly CustomTool[]} tools - The registered tools.
|
|
60
|
+
* @returns {CustomTool[]} Every tool not placed `'review'`, in registration order.
|
|
61
|
+
* @example developerTools(readCustomTools(client))
|
|
62
|
+
*/
|
|
63
|
+
export declare function developerTools(tools: readonly CustomTool[]): CustomTool[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DEVTOOLS_PLUGIN_NAME as e}from"./
|
|
1
|
+
import{DEVTOOLS_PLUGIN_NAME as e}from"./constants.js";function t(e){return typeof e==`object`&&!!e}function n(e){return t(e)&&`frameworkName`in e&&typeof e.frameworkName==`string`&&`component`in e}function r(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function i(e){if(t(e)&&r(e.steps))return r(e.notes)?{steps:e.steps,notes:e.notes}:{steps:e.steps}}function a(e){if(!t(e))return null;let{id:r,label:a,component:o,placement:s,description:c,guide:l}=e;if(typeof r!=`string`||typeof a!=`string`||!n(o))return null;let u={id:r,label:a,component:o};(s===`review`||s===`developer`)&&(u.placement=s),typeof c==`string`&&(u.description=c);let d=i(l);return d&&(u.guide=d),u}function o(t){let n=t.getPluginConfig(e).customTools;if(!Array.isArray(n))return[];let r=new Set;return n.map(a).filter(e=>e!==null).filter(e=>!r.has(e.id)&&(r.add(e.id),!0))}function s(e){return e.filter(e=>e.placement===`review`)}function c(e){return e.filter(e=>e.placement!==`review`)}export{c as developerTools,o as readCustomTools,s as reviewTools};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** How an answer is shown and edited. `system` is read-only. */
|
|
2
|
+
export type AnswerKind = 'options' | 'boolean' | 'text' | 'number' | 'system';
|
|
3
|
+
/** The slice of a registered field this module reads. */
|
|
4
|
+
export interface AnswerField {
|
|
5
|
+
key: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
type: 'text' | 'number' | 'object' | 'boolean';
|
|
9
|
+
multiple: boolean;
|
|
10
|
+
options?: readonly {
|
|
11
|
+
value: unknown;
|
|
12
|
+
label: string;
|
|
13
|
+
}[] | null;
|
|
14
|
+
}
|
|
15
|
+
/** Everything a row of the Answers panel needs about one answer. */
|
|
16
|
+
export interface AnswerView {
|
|
17
|
+
/** The question as the customer met it. */
|
|
18
|
+
title: string;
|
|
19
|
+
/** The short noun, for a summary line where the question is too long. */
|
|
20
|
+
noun: string;
|
|
21
|
+
kind: AnswerKind;
|
|
22
|
+
/** What the answer reads as. `null` when nothing has been answered. */
|
|
23
|
+
display: string | null;
|
|
24
|
+
answered: boolean;
|
|
25
|
+
/** False for `system` answers, which are changed on their own screen. */
|
|
26
|
+
editable: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The short noun for a field, for summary lines and logic sentences.
|
|
30
|
+
*
|
|
31
|
+
* @param {AnswerField} field - The field.
|
|
32
|
+
* @returns {string} The field's name, or its de-slugged key.
|
|
33
|
+
* @example
|
|
34
|
+
* nounOf({ key: 'purpose', name: 'Purpose', type: 'text', multiple: false }) // 'Purpose'
|
|
35
|
+
*/
|
|
36
|
+
export declare function nounOf(field: AnswerField): string;
|
|
37
|
+
/**
|
|
38
|
+
* The question the customer was asked, falling back through the noun to the key.
|
|
39
|
+
*
|
|
40
|
+
* @param {AnswerField} field - The field.
|
|
41
|
+
* @returns {string} The field's label, name or de-slugged key.
|
|
42
|
+
* @example
|
|
43
|
+
* questionOf({ key: 'postcode', label: 'What is your postcode?', type: 'text', multiple: false })
|
|
44
|
+
*/
|
|
45
|
+
export declare function questionOf(field: AnswerField): string;
|
|
46
|
+
/**
|
|
47
|
+
* Classifies a field by how its answer is shown and edited. An object field is `system` whatever
|
|
48
|
+
* else it carries, because an object with options is still machine-written data.
|
|
49
|
+
*
|
|
50
|
+
* @param {AnswerField} field - The field.
|
|
51
|
+
* @returns {AnswerKind} The field's answer kind.
|
|
52
|
+
* @example
|
|
53
|
+
* kindOf({ key: 'quote', type: 'object', multiple: false }) // 'system'
|
|
54
|
+
*/
|
|
55
|
+
export declare function kindOf(field: AnswerField): AnswerKind;
|
|
56
|
+
/**
|
|
57
|
+
* Renders an answer for display. An object value is summarised rather than dumped; the panel
|
|
58
|
+
* offers Copy for the reviewer who wants the payload.
|
|
59
|
+
*
|
|
60
|
+
* @param {unknown} value - The held answer.
|
|
61
|
+
* @param {AnswerField} field - The field it answers.
|
|
62
|
+
* @returns {string | null} The answer in words, or `null` when nothing has been answered.
|
|
63
|
+
* @example
|
|
64
|
+
* renderAnswer(['electricity', 'gas'], services) // 'Electricity, Gas'
|
|
65
|
+
*/
|
|
66
|
+
export declare function renderAnswer(value: unknown, field: AnswerField): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* Everything a row needs about one answer, in one pass.
|
|
69
|
+
*
|
|
70
|
+
* @param {unknown} value - The held answer.
|
|
71
|
+
* @param {AnswerField} field - The field it answers.
|
|
72
|
+
* @returns {AnswerView} The row's view of the answer.
|
|
73
|
+
* @example
|
|
74
|
+
* describeAnswer('moving', purpose).display // 'Moving home'
|
|
75
|
+
*/
|
|
76
|
+
export declare function describeAnswer(value: unknown, field: AnswerField): AnswerView;
|
|
77
|
+
/** The slice of a step this module reads. */
|
|
78
|
+
export interface AskedStep {
|
|
79
|
+
id: string;
|
|
80
|
+
fieldKeys: readonly string[];
|
|
81
|
+
}
|
|
82
|
+
/** The slice of a flow node this module reads. Only step nodes carry a `stepId`. */
|
|
83
|
+
export interface AskedNode {
|
|
84
|
+
type: string;
|
|
85
|
+
stepId?: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Every field a customer can actually be asked: one placed on a screen that some journey's flow
|
|
89
|
+
* reaches. A workspace registers more fields than it asks; code commits the rest, and those sit
|
|
90
|
+
* on no screen or only on a step no flow references, so placement on a FLOW step is what
|
|
91
|
+
* separates them.
|
|
92
|
+
*
|
|
93
|
+
* Some keys ride a screen only so code can read or commit them there, with no input rendering
|
|
94
|
+
* them. The host names those in `codeOwned`, and they are left out too.
|
|
95
|
+
*
|
|
96
|
+
* @param {readonly AskedStep[]} steps - Every registered step.
|
|
97
|
+
* @param {readonly (readonly AskedNode[])[]} flows - Each journey's flow nodes.
|
|
98
|
+
* @param {ReadonlySet<string>} [codeOwned] - Keys a screen carries only for its code.
|
|
99
|
+
* @returns {Set<string>} The keys of every field a journey asks.
|
|
100
|
+
* @example
|
|
101
|
+
* askedFieldKeys(client.steps.listStore.getState(), flows, new Set(['quote_response']))
|
|
102
|
+
*/
|
|
103
|
+
export declare function askedFieldKeys(steps: readonly AskedStep[], flows: readonly (readonly AskedNode[])[], codeOwned?: ReadonlySet<string>): Set<string>;
|
|
104
|
+
/**
|
|
105
|
+
* The raw payload behind a `system` answer, for the Copy button.
|
|
106
|
+
*
|
|
107
|
+
* @param {unknown} value - The held answer.
|
|
108
|
+
* @returns {string} Pretty-printed JSON, or an empty string for an absent value.
|
|
109
|
+
* @example
|
|
110
|
+
* systemPayload({ plans: [] })
|
|
111
|
+
*/
|
|
112
|
+
export declare function systemPayload(value: unknown): string;
|
|
@@ -0,0 +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};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** @file Public entrypoint for how an answer is shown to a reviewer and whether it is editable. */
|
|
2
|
+
export { askedFieldKeys, describeAnswer, kindOf, nounOf, questionOf, renderAnswer, systemPayload, } from './answer-display';
|
|
3
|
+
export type { AnswerField, AnswerKind, AnswerView, AskedNode, AskedStep } from './answer-display';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{askedFieldKeys,describeAnswer,kindOf,nounOf,questionOf,renderAnswer,systemPayload}from"./answer-display.js";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file What the visitor answered, screen by screen, in the order they answered it, as a REDUCER
|
|
3
|
+
* over the SDK's event bus.
|
|
4
|
+
*
|
|
5
|
+
* `responses` says what is held NOW, and code commits much of it. So the history is built from
|
|
6
|
+
* submits instead: on every `step_submit` it records the screen and the values of the fields that
|
|
7
|
+
* screen asks. The bus fires it after the form commits and BEFORE a following action runs, so an
|
|
8
|
+
* action's output never lands in a row.
|
|
9
|
+
*
|
|
10
|
+
* A screen submitted twice (the visitor went Back and changed an answer) is two entries: the
|
|
11
|
+
* second entry is what changed. Session state only, never persisted. Immutable: an event that
|
|
12
|
+
* records nothing answers the SAME array.
|
|
13
|
+
*/
|
|
14
|
+
/** The event shapes this reducer reads, narrowed from the client's `PayloadMap`. */
|
|
15
|
+
export interface HistoryEvent {
|
|
16
|
+
type: string;
|
|
17
|
+
step?: {
|
|
18
|
+
key?: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
fieldKeys?: readonly string[];
|
|
21
|
+
};
|
|
22
|
+
experience?: {
|
|
23
|
+
experienceKey?: string;
|
|
24
|
+
experienceName?: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** One answer as it was at its screen's submit. */
|
|
28
|
+
export interface HistoryAnswer {
|
|
29
|
+
key: string;
|
|
30
|
+
/** The value as it was when the screen was submitted, never re-read later. */
|
|
31
|
+
value: unknown;
|
|
32
|
+
}
|
|
33
|
+
/** One submitted screen and the answers it gave. */
|
|
34
|
+
export interface HistoryEntry {
|
|
35
|
+
/** Unique within the session, for keyed lists. */
|
|
36
|
+
id: number;
|
|
37
|
+
experienceKey: string;
|
|
38
|
+
experienceName: string;
|
|
39
|
+
stepKey: string;
|
|
40
|
+
stepName: string;
|
|
41
|
+
answers: readonly HistoryAnswer[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Whether a value is empty in any shape a field can hold nothing in.
|
|
45
|
+
*
|
|
46
|
+
* @param {unknown} value - The value to test.
|
|
47
|
+
* @returns {boolean} `true` for `undefined`, `null`, `''` and `[]`.
|
|
48
|
+
* @example
|
|
49
|
+
* isBlank([]) // true
|
|
50
|
+
*/
|
|
51
|
+
export declare function isBlank(value: unknown): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* The answers a submitted screen gave: the values of the fields it asks, minus the code-owned
|
|
54
|
+
* ones and the blank ones. Each value is a copy, because an object answer is mutable and a row
|
|
55
|
+
* must keep what was submitted even if a later screen rewrites it.
|
|
56
|
+
*
|
|
57
|
+
* @param {readonly string[]} fieldKeys - The keys the screen asks.
|
|
58
|
+
* @param {Readonly<Record<string, unknown>>} responses - The responses held at the submit.
|
|
59
|
+
* @param {ReadonlySet<string>} [codeOwned] - Keys a screen carries only for its code.
|
|
60
|
+
* @returns {HistoryAnswer[]} The screen's answers, in field order.
|
|
61
|
+
* @example
|
|
62
|
+
* submittedAnswers(['postcode'], { postcode: '3000' }) // [{ key: 'postcode', value: '3000' }]
|
|
63
|
+
*/
|
|
64
|
+
export declare function submittedAnswers(fieldKeys: readonly string[], responses: Readonly<Record<string, unknown>>, codeOwned?: ReadonlySet<string>): HistoryAnswer[];
|
|
65
|
+
/**
|
|
66
|
+
* The history after one event, given the responses held at that moment.
|
|
67
|
+
*
|
|
68
|
+
* @param {readonly HistoryEntry[]} history - The history so far.
|
|
69
|
+
* @param {Readonly<Record<string, unknown>>} responses - The responses held at the event.
|
|
70
|
+
* @param {HistoryEvent} event - The bus event.
|
|
71
|
+
* @param {ReadonlySet<string>} [codeOwned] - Keys a screen carries only for its code.
|
|
72
|
+
* @returns {readonly HistoryEntry[]} The next history, or the same reference when nothing is recorded.
|
|
73
|
+
* @example
|
|
74
|
+
* historyStore.on(bus, (history, event) => nextHistory(history, responses, event))
|
|
75
|
+
*/
|
|
76
|
+
export declare function nextHistory(history: readonly HistoryEntry[], responses: Readonly<Record<string, unknown>>, event: HistoryEvent, codeOwned?: ReadonlySet<string>): readonly HistoryEntry[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return e==null||e===``||Array.isArray(e)&&e.length===0}function t(t,n,r=new Set){return t.filter(t=>!r.has(t)&&!e(n[t])).map(e=>({key:e,value:structuredClone(n[e])}))}function n(e,n,r,i=new Set){if(r.type!==`step_submit`)return e;let a=r.experience?.experienceKey,o=r.step?.key;if(a===void 0||o===void 0||o.length===0)return e;let s=t(r.step?.fieldKeys??[],n,i);if(s.length===0)return e;let c={id:(e.at(-1)?.id??0)+1,experienceKey:a,experienceName:r.experience?.experienceName??a,stepKey:o,stepName:r.step?.name??o,answers:s};return[...e,c].slice(-300)}export{e as isBlank,n as nextHistory,t as submittedAnswers};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{isBlank,nextHistory,submittedAnswers}from"./answer-history.js";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** One section of the guide: a task, its steps, and the tab it is about. */
|
|
2
|
+
export interface GuideSection {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
/** Numbered steps, in order. `**text**` marks a control's label; see {@link emphasis}. */
|
|
6
|
+
steps: readonly string[];
|
|
7
|
+
/** Short lines after the steps. */
|
|
8
|
+
notes: readonly string[];
|
|
9
|
+
/**
|
|
10
|
+
* The sidebar tab this section is about, offered as an "Open …" button: a review tab id or a
|
|
11
|
+
* custom tool's id. Null for a section about no tab.
|
|
12
|
+
*/
|
|
13
|
+
tab: string | null;
|
|
14
|
+
}
|
|
15
|
+
/** A common problem and what to do about it. */
|
|
16
|
+
export interface GuideFix {
|
|
17
|
+
problem: string;
|
|
18
|
+
fix: string;
|
|
19
|
+
}
|
|
20
|
+
/** Which optional tabs this build shows. The guide covers exactly those. */
|
|
21
|
+
export interface GuideAvailability {
|
|
22
|
+
variations: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** The host's own sections, placed around the SDK's. */
|
|
25
|
+
export interface GuideExtras {
|
|
26
|
+
/** Sections read before the SDK's tabs, such as how to report an issue. */
|
|
27
|
+
lead?: readonly GuideSection[];
|
|
28
|
+
/** Sections read after the SDK's tabs, one per custom tool. */
|
|
29
|
+
tools?: readonly GuideSection[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The guide's sections, in reading order: the host's lead sections, the SDK's sections for the
|
|
33
|
+
* tabs this build shows, then the host's tool sections.
|
|
34
|
+
*
|
|
35
|
+
* @param {GuideAvailability} available - Which optional tabs this build shows.
|
|
36
|
+
* @param {GuideExtras} [extra] - The host's own sections.
|
|
37
|
+
* @returns {GuideSection[]} The sections to render.
|
|
38
|
+
* @example
|
|
39
|
+
* guideSections({ variations: true }, { tools: [{ id: 'cart', title: 'Inspect the cart', steps: [], notes: [], tab: 'cart' }] })
|
|
40
|
+
*/
|
|
41
|
+
export declare function guideSections(available: GuideAvailability, extra?: GuideExtras): GuideSection[];
|
|
42
|
+
/**
|
|
43
|
+
* Common problems and what to do, minus any fix that points at a tab this build lacks, followed
|
|
44
|
+
* by the host's own.
|
|
45
|
+
*
|
|
46
|
+
* @param {GuideAvailability} available - Which optional tabs this build shows.
|
|
47
|
+
* @param {readonly GuideFix[]} [extra] - The host's own fixes.
|
|
48
|
+
* @returns {GuideFix[]} The fixes to render.
|
|
49
|
+
* @example
|
|
50
|
+
* guideFixes({ variations: true }, [{ problem: 'The report did not send', fix: 'Try again.' }])
|
|
51
|
+
*/
|
|
52
|
+
export declare function guideFixes(available: GuideAvailability, extra?: readonly GuideFix[]): GuideFix[];
|
|
53
|
+
/** One run of a guide line: plain or bold. */
|
|
54
|
+
export interface TextRun {
|
|
55
|
+
text: string;
|
|
56
|
+
bold: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Splits `**label**` markers into runs, so a control's name renders bold without raw HTML. An
|
|
60
|
+
* unclosed marker stays literal rather than bolding the rest of the line.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} line - A guide line.
|
|
63
|
+
* @returns {TextRun[]} The line's runs, in order.
|
|
64
|
+
* @example
|
|
65
|
+
* emphasis('Click **Save**.') // [{ text: 'Click ', bold: false }, { text: 'Save', bold: true }, { text: '.', bold: false }]
|
|
66
|
+
*/
|
|
67
|
+
export declare function emphasis(line: string): TextRun[];
|
|
@@ -0,0 +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:`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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{emphasis,guideFixes,guideSections}from"./guide-sections.js";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Public entrypoint for the review tools' pure derivations. Every panel stays thin by pushing
|
|
3
|
+
* its thinking here, where it is tested off plain objects with no client, no DOM and no store.
|
|
4
|
+
*/
|
|
5
|
+
export { askedFieldKeys, describeAnswer, kindOf, nounOf, questionOf, renderAnswer, systemPayload, } from './answer-display';
|
|
6
|
+
export type { AnswerField, AnswerKind, AnswerView, AskedNode, AskedStep } from './answer-display';
|
|
7
|
+
export { isBlank, nextHistory, submittedAnswers } from './answer-history';
|
|
8
|
+
export type { HistoryAnswer, HistoryEntry, HistoryEvent } from './answer-history';
|
|
9
|
+
export { emphasis, guideFixes, guideSections } from './guide-sections';
|
|
10
|
+
export type { GuideAvailability, GuideExtras, GuideFix, GuideSection, TextRun, } from './guide-sections';
|
|
11
|
+
export { buildOutline } from './journey-outline';
|
|
12
|
+
export type { OutlineCondition, OutlineEdge, OutlineInput, OutlineNode, OutlineRow, ScreenState, } from './journey-outline';
|
|
13
|
+
export { nextOpenOrder, pickJourney } from './journey-pick';
|
|
14
|
+
export type { InstanceLike, JourneyPickOptions, OpenOrderEvent } from './journey-pick';
|
|
15
|
+
export { COLOURS, LANE_WIDTH, ROW_HEIGHT, buildTimeline } from './journey-timeline';
|
|
16
|
+
export type { TimelineEdge, TimelineLayout, TimelineNext, TimelineNode } from './journey-timeline';
|
|
17
|
+
export { BREADTH, DEPTH, buildTree } from './journey-tree';
|
|
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';
|
|
20
|
+
export type { ReviewTabId } from './plain-language';
|
|
21
|
+
export { UNDESCRIBABLE, describeRule, varKeyOf } from './plain-logic';
|
|
22
|
+
export type { FieldLookup, FieldSummary } from './plain-logic';
|
|
23
|
+
export { derivePosture } from './safety-posture';
|
|
24
|
+
export type { PostureOptions, SafetyPosture } from './safety-posture';
|
|
25
|
+
export { nextTrails } from './visited-steps';
|
|
26
|
+
export type { Trails, VisitedEvent } from './visited-steps';
|
|
27
|
+
export { nextRoute } from './walk-route';
|
|
28
|
+
export type { RouteEntry } from './walk-route';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DEVELOPER_LABEL as e,NODE_LABELS as t,TAB_INTROS as n,TAB_LABELS as r,configSourceLabel as i,deslug as a,screenName as o}from"./plain-language/plain-language.js";import{askedFieldKeys as s,describeAnswer as c,kindOf as l,nounOf as u,questionOf as d,renderAnswer as f,systemPayload as p}from"./answer-display/answer-display.js";import{isBlank as m,nextHistory as h,submittedAnswers as g}from"./answer-history/answer-history.js";import{emphasis as _,guideFixes as v,guideSections as y}from"./guide-sections/guide-sections.js";import{UNDESCRIBABLE as b,describeRule as x,varKeyOf as S}from"./plain-logic/plain-logic.js";import{buildOutline as C}from"./journey-outline/journey-outline.js";import{nextOpenOrder as w,pickJourney as T}from"./journey-pick/journey-pick.js";import{BREADTH as E,DEPTH as D,buildTree as O}from"./journey-tree/journey-tree.js";import"./journey-tree/index.js";import{COLOURS as k,LANE_WIDTH as A,ROW_HEIGHT as j,buildTimeline as M}from"./journey-timeline/journey-timeline.js";import"./journey-timeline/index.js";import{derivePosture as N}from"./safety-posture/safety-posture.js";import{nextTrails as P}from"./visited-steps/visited-steps.js";import{nextRoute as F}from"./walk-route/walk-route.js";export{E as BREADTH,k as COLOURS,D as DEPTH,e as DEVELOPER_LABEL,A as LANE_WIDTH,t as NODE_LABELS,j as ROW_HEIGHT,n as TAB_INTROS,r as TAB_LABELS,b as UNDESCRIBABLE,s as askedFieldKeys,C as buildOutline,M as buildTimeline,O as buildTree,i as configSourceLabel,N as derivePosture,c as describeAnswer,x as describeRule,a as deslug,_ as emphasis,v as guideFixes,y as guideSections,m as isBlank,l as kindOf,h as nextHistory,w as nextOpenOrder,F as nextRoute,P as nextTrails,u as nounOf,T as pickJourney,d as questionOf,f as renderAnswer,o as screenName,g as submittedAnswers,p as systemPayload,S as varKeyOf};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{buildOutline}from"./journey-outline.js";
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { FieldLookup } from '../plain-logic';
|
|
2
|
+
/** The slice of a `treeStore` node this module reads, narrowed so tests can build plain fixtures. */
|
|
3
|
+
export interface OutlineNode {
|
|
4
|
+
id: string;
|
|
5
|
+
type: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
stepKey?: string;
|
|
8
|
+
conditionId?: string;
|
|
9
|
+
actionKey?: string;
|
|
10
|
+
experienceKey?: string;
|
|
11
|
+
}
|
|
12
|
+
/** The slice of a `treeStore` edge this module reads. */
|
|
13
|
+
export interface OutlineEdge {
|
|
14
|
+
id: string;
|
|
15
|
+
sourceNodeId: string;
|
|
16
|
+
destinationNodeId: string;
|
|
17
|
+
sourceHandle?: string;
|
|
18
|
+
}
|
|
19
|
+
/** The slice of a registered condition this module reads. */
|
|
20
|
+
export interface OutlineCondition {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
logic: unknown;
|
|
24
|
+
}
|
|
25
|
+
/** Where a screen sits relative to the visitor. */
|
|
26
|
+
export type ScreenState = 'visited' | 'current' | 'ahead';
|
|
27
|
+
/** One row of the outline. */
|
|
28
|
+
export type OutlineRow = {
|
|
29
|
+
kind: 'start';
|
|
30
|
+
id: string;
|
|
31
|
+
depth: number;
|
|
32
|
+
label: string;
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'screen';
|
|
35
|
+
id: string;
|
|
36
|
+
depth: number;
|
|
37
|
+
label: string;
|
|
38
|
+
stepKey: string | null;
|
|
39
|
+
state: ScreenState;
|
|
40
|
+
} | {
|
|
41
|
+
kind: 'decision';
|
|
42
|
+
id: string;
|
|
43
|
+
depth: number;
|
|
44
|
+
label: string;
|
|
45
|
+
rule: string;
|
|
46
|
+
} | {
|
|
47
|
+
kind: 'arm';
|
|
48
|
+
id: string;
|
|
49
|
+
depth: number;
|
|
50
|
+
label: string;
|
|
51
|
+
} | {
|
|
52
|
+
kind: 'end';
|
|
53
|
+
id: string;
|
|
54
|
+
depth: number;
|
|
55
|
+
label: string;
|
|
56
|
+
} | {
|
|
57
|
+
kind: 'journey';
|
|
58
|
+
id: string;
|
|
59
|
+
depth: number;
|
|
60
|
+
label: string;
|
|
61
|
+
} | {
|
|
62
|
+
kind: 'backref';
|
|
63
|
+
id: string;
|
|
64
|
+
depth: number;
|
|
65
|
+
label: string;
|
|
66
|
+
};
|
|
67
|
+
/** Everything {@link buildOutline} reads. */
|
|
68
|
+
export interface OutlineInput {
|
|
69
|
+
nodes: readonly OutlineNode[];
|
|
70
|
+
edges: readonly OutlineEdge[];
|
|
71
|
+
conditions: readonly OutlineCondition[];
|
|
72
|
+
fieldLookup: FieldLookup;
|
|
73
|
+
/** Step keys the visitor has entered this session. */
|
|
74
|
+
visited: ReadonlySet<string>;
|
|
75
|
+
/** The step the visitor is on right now. */
|
|
76
|
+
currentStepKey: string | null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Folds the graph into rows. Pure: everything it needs arrives as an argument.
|
|
80
|
+
*
|
|
81
|
+
* @param {OutlineInput} input - The flow, its conditions, the field index and the visitor's trail.
|
|
82
|
+
* @returns {OutlineRow[]} The outline's rows, in reading order.
|
|
83
|
+
* @example
|
|
84
|
+
* buildOutline({ nodes, edges, conditions, fieldLookup, visited, currentStepKey: 'postcode' })
|
|
85
|
+
*/
|
|
86
|
+
export declare function buildOutline({ nodes, edges, conditions, fieldLookup, visited, currentStepKey, }: OutlineInput): OutlineRow[];
|
|
@@ -0,0 +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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{nextOpenOrder,pickJourney}from"./journey-pick.js";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Which open experience instance the reviewer is looking at.
|
|
3
|
+
*
|
|
4
|
+
* Several instances can be open at once (an inline entry widget under a full-screen overlay, a
|
|
5
|
+
* modal over both). The host knows its own stacking, so it names the experiences that sit on top
|
|
6
|
+
* in `precedence`, topmost first. Without a match there, the instance opened most recently wins,
|
|
7
|
+
* which is the one a new overlay puts in front. The record of what opened when is kept from the
|
|
8
|
+
* event bus by {@link nextOpenOrder}.
|
|
9
|
+
*/
|
|
10
|
+
/** The slice of an `ExperienceInstance` the pick reads, narrow so tests can build fakes. */
|
|
11
|
+
export interface InstanceLike {
|
|
12
|
+
id: string;
|
|
13
|
+
experienceKey: string;
|
|
14
|
+
experienceName: string;
|
|
15
|
+
isOpenStore: {
|
|
16
|
+
getState: () => boolean;
|
|
17
|
+
};
|
|
18
|
+
currentStepStore: {
|
|
19
|
+
getState: () => {
|
|
20
|
+
key: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
} | null;
|
|
23
|
+
};
|
|
24
|
+
currentNodeStore: {
|
|
25
|
+
getState: () => {
|
|
26
|
+
type: string;
|
|
27
|
+
} | null;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** The host's say on which instance is in front. */
|
|
31
|
+
export interface JourneyPickOptions {
|
|
32
|
+
/** Experience keys never picked: the devtools themselves, and any the host hides. */
|
|
33
|
+
exclude: ReadonlySet<string>;
|
|
34
|
+
/** Experience keys, topmost first. */
|
|
35
|
+
precedence: readonly string[];
|
|
36
|
+
/** Instance ids in the order they opened, oldest first. */
|
|
37
|
+
openOrder: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
/** The bus event shapes {@link nextOpenOrder} reads, narrowed from the client's `PayloadMap`. */
|
|
40
|
+
export interface OpenOrderEvent {
|
|
41
|
+
type: string;
|
|
42
|
+
experience?: {
|
|
43
|
+
id?: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The open instance the reviewer is looking at. Among the open instances whose experience is not
|
|
48
|
+
* excluded: the first match by `precedence`, else the one that appears latest in `openOrder`, else
|
|
49
|
+
* the last one in list order.
|
|
50
|
+
*
|
|
51
|
+
* @template {InstanceLike} T - The caller's instance type, handed back whole.
|
|
52
|
+
* @param {readonly T[]} instances - Every experience instance.
|
|
53
|
+
* @param {JourneyPickOptions} options - Exclusions, precedence and the order instances opened in.
|
|
54
|
+
* @returns {T | undefined} The instance in front, or `undefined` when none is open.
|
|
55
|
+
* @example
|
|
56
|
+
* pickJourney(client.experienceInstances.listStore.getState(), {
|
|
57
|
+
* exclude: new Set(['devtools']),
|
|
58
|
+
* precedence: ['checkout-modal'],
|
|
59
|
+
* openOrder,
|
|
60
|
+
* })
|
|
61
|
+
*/
|
|
62
|
+
export declare function pickJourney<T extends InstanceLike>(instances: readonly T[], options: JourneyPickOptions): T | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* The open order after one bus event. `experience_open` moves the instance's id to the end (an
|
|
65
|
+
* id appears once), and `experience_close` removes it.
|
|
66
|
+
*
|
|
67
|
+
* @param {readonly string[]} order - Instance ids in the order they opened, oldest first.
|
|
68
|
+
* @param {OpenOrderEvent} event - The bus event.
|
|
69
|
+
* @returns {readonly string[]} The next order, or the same reference when the event changes nothing.
|
|
70
|
+
* @example
|
|
71
|
+
* createStore<readonly string[]>([]).on(client.eventBus, nextOpenOrder)
|
|
72
|
+
*/
|
|
73
|
+
export declare function nextOpenOrder(order: readonly string[], event: OpenOrderEvent): readonly string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e,t){let n=e.filter(e=>!t.exclude.has(e.experienceKey)&&e.isOpenStore.getState());for(let e of t.precedence){let t=n.find(t=>t.experienceKey===e);if(t!==void 0)return t}let r,i=-1;for(let e of n){let n=t.openOrder.lastIndexOf(e.id);n>i&&(r=e,i=n)}return r??n.at(-1)}function t(e,t){let n=t.experience?.id;return n===void 0||n.length===0?e:t.type===`experience_open`?e.at(-1)===n?e:[...e.filter(e=>e!==n),n]:t.type===`experience_close`&&e.includes(n)?e.filter(e=>e!==n):e}export{t as nextOpenOrder,e as pickJourney};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{COLOURS,LANE_WIDTH,ROW_HEIGHT,buildTimeline}from"./journey-timeline.js";
|