@daltonr/pathwrite-solid 0.13.1 → 0.14.1
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 +22 -0
- package/dist/index.css +12 -4
- package/dist/index.d.ts +10 -9
- package/dist/index.js +553 -240
- package/dist/index.js.map +1 -1
- package/dist/server.js +441 -0
- package/dist/server.js.map +1 -0
- package/package.json +7 -4
- package/src/index.tsx +182 -122
package/src/index.tsx
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
// Types
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
33
|
|
|
34
|
-
export interface UsePathOptions {
|
|
34
|
+
export interface UsePathOptions<TData extends PathData = PathData> {
|
|
35
35
|
/**
|
|
36
36
|
* An externally-managed `PathEngine` to subscribe to — for example, the engine
|
|
37
37
|
* returned by `restoreOrStart()` from `@daltonr/pathwrite-store`.
|
|
@@ -48,9 +48,9 @@ export interface UsePathOptions {
|
|
|
48
48
|
* async `restoreOrStart()`) or is swapped is adopted — the hook
|
|
49
49
|
* re-subscribes and re-seeds its snapshot from the new engine.
|
|
50
50
|
*/
|
|
51
|
-
engine?: PathEngine | Accessor<PathEngine | undefined>;
|
|
51
|
+
engine?: PathEngine<TData> | Accessor<PathEngine<TData> | undefined>;
|
|
52
52
|
/** Called for every engine event (stateChanged, completed, cancelled, resumed). */
|
|
53
|
-
onEvent?: (event: PathEvent) => void;
|
|
53
|
+
onEvent?: (event: PathEvent<TData>) => void;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export interface UsePathReturn<TData extends PathData = PathData> {
|
|
@@ -60,9 +60,13 @@ export interface UsePathReturn<TData extends PathData = PathData> {
|
|
|
60
60
|
*/
|
|
61
61
|
snapshot: Accessor<PathSnapshot<TData> | null>;
|
|
62
62
|
/** Start (or restart) a path. */
|
|
63
|
-
start: (path: PathDefinition<
|
|
64
|
-
/** Push a sub-path onto the stack. Requires an active path. Pass an optional `meta` object for correlation — it is returned unchanged to the parent step's `onSubPathComplete` / `onSubPathCancel` hooks. */
|
|
65
|
-
startSubPath: (
|
|
63
|
+
start: (path: PathDefinition<TData>, initialData?: Partial<TData>) => Promise<void>;
|
|
64
|
+
/** Push a sub-path onto the stack. Requires an active path. A sub-path has its own data, so any definition is accepted. Pass an optional `meta` object for correlation — it is returned unchanged to the parent step's `onSubPathComplete` / `onSubPathCancel` hooks. */
|
|
65
|
+
startSubPath: (
|
|
66
|
+
path: PathDefinition,
|
|
67
|
+
initialData?: PathData,
|
|
68
|
+
meta?: Record<string, unknown>
|
|
69
|
+
) => Promise<void>;
|
|
66
70
|
/** Advance one step. Completes the path on the last step. */
|
|
67
71
|
next: () => Promise<void>;
|
|
68
72
|
/** Go back one step. No-op when already on the first step of a top-level path. Pops back to the parent path when on the first step of a sub-path. */
|
|
@@ -96,55 +100,68 @@ export interface UsePathReturn<TData extends PathData = PathData> {
|
|
|
96
100
|
// usePath composable
|
|
97
101
|
// ---------------------------------------------------------------------------
|
|
98
102
|
|
|
99
|
-
export function usePath<TData extends PathData = PathData>(
|
|
100
|
-
|
|
101
|
-
|
|
103
|
+
export function usePath<TData extends PathData = PathData>(
|
|
104
|
+
options?: UsePathOptions<TData>
|
|
105
|
+
): UsePathReturn<TData> {
|
|
106
|
+
let ownEngine: PathEngine<TData> | null = null;
|
|
107
|
+
const resolveEngine = (): PathEngine<TData> => {
|
|
102
108
|
const external = typeof options?.engine === "function" ? options.engine() : options?.engine;
|
|
103
|
-
return external ?? (ownEngine ??= new PathEngine());
|
|
109
|
+
return external ?? (ownEngine ??= new PathEngine<TData>());
|
|
104
110
|
};
|
|
105
111
|
let engine = resolveEngine();
|
|
106
112
|
|
|
107
113
|
const [snapshot, setSnapshot] = createSignal<PathSnapshot<TData> | null>(
|
|
108
|
-
engine.snapshot()
|
|
114
|
+
engine.snapshot(),
|
|
109
115
|
// always notify — PathEngine produces new snapshot objects on every event
|
|
110
116
|
{ equals: false }
|
|
111
117
|
);
|
|
112
118
|
|
|
113
|
-
const onEngineEvent = (event: PathEvent): void => {
|
|
119
|
+
const onEngineEvent = (event: PathEvent<TData>): void => {
|
|
114
120
|
if (event.type === "stateChanged" || event.type === "resumed") {
|
|
115
|
-
setSnapshot(event.snapshot
|
|
121
|
+
setSnapshot(event.snapshot);
|
|
116
122
|
} else if (event.type === "completed" || event.type === "cancelled") {
|
|
117
|
-
setSnapshot(engine.snapshot()
|
|
123
|
+
setSnapshot(engine.snapshot());
|
|
118
124
|
}
|
|
119
125
|
options?.onEvent?.(event);
|
|
120
126
|
};
|
|
121
127
|
let unsubscribe = engine.subscribe(onEngineEvent);
|
|
122
128
|
|
|
123
129
|
// Adopt a late or swapped engine: re-subscribe and re-seed the snapshot.
|
|
124
|
-
createEffect(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
createEffect(
|
|
131
|
+
on(
|
|
132
|
+
resolveEngine,
|
|
133
|
+
(next) => {
|
|
134
|
+
if (next === engine) return;
|
|
135
|
+
unsubscribe();
|
|
136
|
+
engine = next;
|
|
137
|
+
setSnapshot(engine.snapshot());
|
|
138
|
+
unsubscribe = engine.subscribe(onEngineEvent);
|
|
139
|
+
},
|
|
140
|
+
{ defer: true }
|
|
141
|
+
)
|
|
142
|
+
);
|
|
131
143
|
|
|
132
144
|
onCleanup(() => unsubscribe());
|
|
133
145
|
|
|
134
|
-
const start = (path: PathDefinition<
|
|
146
|
+
const start = (path: PathDefinition<TData>, initialData: Partial<TData> = {}): Promise<void> =>
|
|
135
147
|
engine.start(path, initialData);
|
|
136
148
|
|
|
137
|
-
const startSubPath = (
|
|
138
|
-
|
|
149
|
+
const startSubPath = (
|
|
150
|
+
path: PathDefinition,
|
|
151
|
+
initialData: PathData = {},
|
|
152
|
+
meta?: Record<string, unknown>
|
|
153
|
+
): Promise<void> => engine.startSubPath(path, initialData, meta);
|
|
139
154
|
|
|
140
155
|
const next = (): Promise<void> => engine.next();
|
|
141
156
|
const previous = (): Promise<void> => engine.previous();
|
|
142
157
|
const cancel = (): Promise<void> => engine.cancel();
|
|
143
|
-
const goToStep = (stepId: string, options?: { validateOnLeave?: boolean }): Promise<void> =>
|
|
144
|
-
|
|
158
|
+
const goToStep = (stepId: string, options?: { validateOnLeave?: boolean }): Promise<void> =>
|
|
159
|
+
engine.goToStep(stepId, options);
|
|
160
|
+
const goToStepChecked = (stepId: string, options?: { validateOnLeave?: boolean }): Promise<void> =>
|
|
161
|
+
engine.goToStepChecked(stepId, options);
|
|
145
162
|
|
|
146
|
-
const setData =
|
|
147
|
-
engine.setData(key, value
|
|
163
|
+
const setData = <K extends string & keyof TData>(key: K, value: TData[K]): Promise<void> =>
|
|
164
|
+
engine.setData(key, value);
|
|
148
165
|
|
|
149
166
|
const resetStep = (): Promise<void> => engine.resetStep();
|
|
150
167
|
const restart = (): Promise<void> => engine.restart();
|
|
@@ -152,7 +169,22 @@ export function usePath<TData extends PathData = PathData>(options?: UsePathOpti
|
|
|
152
169
|
const suspend = (): Promise<void> => engine.suspend();
|
|
153
170
|
const validate = (): void => engine.validate();
|
|
154
171
|
|
|
155
|
-
return {
|
|
172
|
+
return {
|
|
173
|
+
snapshot,
|
|
174
|
+
start,
|
|
175
|
+
startSubPath,
|
|
176
|
+
next,
|
|
177
|
+
previous,
|
|
178
|
+
cancel,
|
|
179
|
+
goToStep,
|
|
180
|
+
goToStepChecked,
|
|
181
|
+
setData,
|
|
182
|
+
resetStep,
|
|
183
|
+
restart,
|
|
184
|
+
retry,
|
|
185
|
+
suspend,
|
|
186
|
+
validate,
|
|
187
|
+
};
|
|
156
188
|
}
|
|
157
189
|
|
|
158
190
|
// ---------------------------------------------------------------------------
|
|
@@ -174,13 +206,18 @@ const PathContext = createContext<PathContextValue | undefined>(undefined);
|
|
|
174
206
|
* - `TData` narrows `snapshot().data`
|
|
175
207
|
* - `TServices` types the `services` value — must match what was passed to `PathShell`
|
|
176
208
|
*/
|
|
177
|
-
export function usePathContext<TData extends PathData = PathData, TServices = unknown>(): Omit<
|
|
209
|
+
export function usePathContext<TData extends PathData = PathData, TServices = unknown>(): Omit<
|
|
210
|
+
UsePathReturn<TData>,
|
|
211
|
+
"snapshot"
|
|
212
|
+
> & { snapshot: Accessor<PathSnapshot<TData>>; services: TServices } {
|
|
178
213
|
const ctx = useContext(PathContext);
|
|
179
214
|
if (!ctx) {
|
|
180
215
|
throw new Error("usePathContext must be used within a PathShell component.");
|
|
181
216
|
}
|
|
182
217
|
return {
|
|
183
|
-
...(ctx.path as unknown as Omit<UsePathReturn<TData>, "snapshot"> & {
|
|
218
|
+
...(ctx.path as unknown as Omit<UsePathReturn<TData>, "snapshot"> & {
|
|
219
|
+
snapshot: Accessor<PathSnapshot<TData>>;
|
|
220
|
+
}),
|
|
184
221
|
services: ctx.services as TServices,
|
|
185
222
|
};
|
|
186
223
|
}
|
|
@@ -202,7 +239,8 @@ export interface PathShellActions {
|
|
|
202
239
|
}
|
|
203
240
|
|
|
204
241
|
export interface PathShellProps {
|
|
205
|
-
path
|
|
242
|
+
/** The path to run. The shell is not typed over the path's data, so a definition of any data type is accepted. */
|
|
243
|
+
path: PathDefinition;
|
|
206
244
|
/**
|
|
207
245
|
* An externally-managed engine — for example, the engine returned by
|
|
208
246
|
* `restoreOrStart()` from `@daltonr/pathwrite-store`. When supplied, `PathShell` will skip its own
|
|
@@ -294,7 +332,8 @@ export const PathShell: Component<PathShellProps> = (props) => {
|
|
|
294
332
|
// step (which re-ran onEnter/onLeave and lost attempted / visited state).
|
|
295
333
|
const restoredEngine: PathEngine | null = (() => {
|
|
296
334
|
if (props.engine || !props.restoreKey || !outerCtx) return null;
|
|
297
|
-
const stored = outerCtx.path.snapshot()?.data[props.restoreKey] as
|
|
335
|
+
const stored = outerCtx.path.snapshot()?.data[props.restoreKey] as
|
|
336
|
+
{ serializedState?: SerializedPathState } | undefined;
|
|
298
337
|
if (!stored || typeof stored !== "object" || !stored.serializedState) return null;
|
|
299
338
|
try {
|
|
300
339
|
return PathEngine.fromState(stored.serializedState, { [props.path.id]: props.path });
|
|
@@ -314,14 +353,28 @@ export const PathShell: Component<PathShellProps> = (props) => {
|
|
|
314
353
|
if (event.type === "completed") props.onComplete?.(event.data as PathData);
|
|
315
354
|
if (event.type === "cancelled") props.onCancel?.(event.data as PathData);
|
|
316
355
|
if (props.restoreKey && outerCtx && event.type === "stateChanged") {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
356
|
+
void outerCtx.path.setData(props.restoreKey, {
|
|
357
|
+
...event.snapshot,
|
|
358
|
+
serializedState: currentEngine().exportState(),
|
|
359
|
+
});
|
|
320
360
|
}
|
|
321
361
|
},
|
|
322
362
|
});
|
|
323
363
|
|
|
324
|
-
const {
|
|
364
|
+
const {
|
|
365
|
+
snapshot,
|
|
366
|
+
start,
|
|
367
|
+
next,
|
|
368
|
+
previous,
|
|
369
|
+
cancel,
|
|
370
|
+
goToStep,
|
|
371
|
+
goToStepChecked,
|
|
372
|
+
setData,
|
|
373
|
+
restart,
|
|
374
|
+
retry,
|
|
375
|
+
suspend,
|
|
376
|
+
validate,
|
|
377
|
+
} = pathReturn;
|
|
325
378
|
|
|
326
379
|
onMount(() => {
|
|
327
380
|
if (props.autoStart !== false && !props.engine && !restoredEngine) {
|
|
@@ -348,8 +401,12 @@ export const PathShell: Component<PathShellProps> = (props) => {
|
|
|
348
401
|
const contextValue: PathContextValue = { path: pathReturn, services: props.services ?? null };
|
|
349
402
|
|
|
350
403
|
const actions: PathShellActions = {
|
|
351
|
-
next,
|
|
352
|
-
|
|
404
|
+
next,
|
|
405
|
+
previous,
|
|
406
|
+
cancel,
|
|
407
|
+
goToStep,
|
|
408
|
+
goToStepChecked,
|
|
409
|
+
setData,
|
|
353
410
|
restart: () => restart(),
|
|
354
411
|
retry: () => retry(),
|
|
355
412
|
suspend: () => suspend(),
|
|
@@ -367,13 +424,16 @@ export const PathShell: Component<PathShellProps> = (props) => {
|
|
|
367
424
|
|
|
368
425
|
const effectiveHideProgress = () => props.hideProgress || props.layout === "tabs";
|
|
369
426
|
const effectiveHideFooter = () => props.hideFooter || props.layout === "tabs";
|
|
370
|
-
const showRoot = () =>
|
|
427
|
+
const showRoot = () =>
|
|
428
|
+
!effectiveHideProgress() && !!snap().rootProgress && props.progressLayout !== "activeOnly";
|
|
371
429
|
// A custom header is the consumer's decision: show it whenever progress is
|
|
372
430
|
// not hidden, even for a single-step path. Only the *default* header hides
|
|
373
431
|
// for one step (same rule as the React / Vue shells).
|
|
374
|
-
const showActive = () =>
|
|
375
|
-
|
|
376
|
-
|
|
432
|
+
const showActive = () =>
|
|
433
|
+
!effectiveHideProgress() &&
|
|
434
|
+
(props.renderHeader
|
|
435
|
+
? true
|
|
436
|
+
: (snap().stepCount > 1 || snap().nestingLevel > 0) && props.progressLayout !== "rootOnly");
|
|
377
437
|
|
|
378
438
|
// The step render function must only run when the *step* changes. The
|
|
379
439
|
// snapshot signal is `{ equals: false }` (a new object on every engine
|
|
@@ -401,8 +461,14 @@ export const PathShell: Component<PathShellProps> = (props) => {
|
|
|
401
461
|
// current values reactively even though the step itself is created once.
|
|
402
462
|
const liveSnapshot = new Proxy({} as PathSnapshot, {
|
|
403
463
|
get: (_target, key) => (snapshot() as unknown as Record<PropertyKey, unknown> | null)?.[key],
|
|
404
|
-
has: (_target, key) => {
|
|
405
|
-
|
|
464
|
+
has: (_target, key) => {
|
|
465
|
+
const s = snapshot();
|
|
466
|
+
return s ? key in s : false;
|
|
467
|
+
},
|
|
468
|
+
ownKeys: () => {
|
|
469
|
+
const s = snapshot();
|
|
470
|
+
return s ? Reflect.ownKeys(s) : [];
|
|
471
|
+
},
|
|
406
472
|
getOwnPropertyDescriptor: (_target, key) => {
|
|
407
473
|
const s = snapshot();
|
|
408
474
|
const d = s ? Object.getOwnPropertyDescriptor(s, key) : undefined;
|
|
@@ -427,8 +493,7 @@ export const PathShell: Component<PathShellProps> = (props) => {
|
|
|
427
493
|
Object.keys(snap().fieldErrors).length > 0;
|
|
428
494
|
|
|
429
495
|
const showWarnings = () =>
|
|
430
|
-
props.validationDisplay !== "inline" &&
|
|
431
|
-
Object.keys(snap().fieldWarnings).length > 0;
|
|
496
|
+
props.validationDisplay !== "inline" && Object.keys(snap().fieldWarnings).length > 0;
|
|
432
497
|
|
|
433
498
|
const showBlockingError = () =>
|
|
434
499
|
props.validationDisplay !== "inline" &&
|
|
@@ -469,75 +534,71 @@ export const PathShell: Component<PathShellProps> = (props) => {
|
|
|
469
534
|
</Show>
|
|
470
535
|
{/* Header — progress (active path) */}
|
|
471
536
|
<Show when={showActive()}>
|
|
472
|
-
{props.renderHeader
|
|
473
|
-
? props.renderHeader(snap())
|
|
474
|
-
: <SolidHeader snapshot={snap()} />}
|
|
537
|
+
{props.renderHeader ? props.renderHeader(snap()) : <SolidHeader snapshot={snap()} />}
|
|
475
538
|
</Show>
|
|
476
539
|
{/* Completion panel — shown when path finishes with stayOnFinal */}
|
|
477
540
|
<Show when={snap().status === "completed"}>
|
|
478
541
|
<div class="pw-shell__body">
|
|
479
|
-
{props.completionContent
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
Start over
|
|
490
|
-
</button>
|
|
491
|
-
</div>
|
|
492
|
-
)
|
|
493
|
-
}
|
|
542
|
+
{props.completionContent ? (
|
|
543
|
+
props.completionContent(snap())
|
|
544
|
+
) : (
|
|
545
|
+
<div class="pw-shell__completion">
|
|
546
|
+
<p class="pw-shell__completion-message">All done.</p>
|
|
547
|
+
<button type="button" class="pw-shell__completion-restart" onClick={() => restart()}>
|
|
548
|
+
Start over
|
|
549
|
+
</button>
|
|
550
|
+
</div>
|
|
551
|
+
)}
|
|
494
552
|
</div>
|
|
495
553
|
</Show>
|
|
496
554
|
{/* Body — step content (hidden when completed) */}
|
|
497
555
|
<Show when={snap().status !== "completed"}>
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
556
|
+
<div class="pw-shell__body">
|
|
557
|
+
<StepContent />
|
|
558
|
+
</div>
|
|
559
|
+
{/* Validation messages */}
|
|
560
|
+
<Show when={showValidation()}>
|
|
561
|
+
<ul class="pw-shell__validation">
|
|
562
|
+
<For each={Object.entries(snap().fieldErrors)}>
|
|
563
|
+
{([key, msg]) => (
|
|
564
|
+
<li class="pw-shell__validation-item">
|
|
565
|
+
<Show when={key !== "_"}>
|
|
566
|
+
<span class="pw-shell__validation-label">{formatFieldKey(key)}</span>
|
|
567
|
+
</Show>
|
|
568
|
+
{msg}
|
|
569
|
+
</li>
|
|
570
|
+
)}
|
|
571
|
+
</For>
|
|
572
|
+
</ul>
|
|
573
|
+
</Show>
|
|
574
|
+
{/* Warning messages — non-blocking, shown immediately */}
|
|
575
|
+
<Show when={showWarnings()}>
|
|
576
|
+
<ul class="pw-shell__warnings">
|
|
577
|
+
<For each={Object.entries(snap().fieldWarnings)}>
|
|
578
|
+
{([key, msg]) => (
|
|
579
|
+
<li class="pw-shell__warnings-item">
|
|
580
|
+
<Show when={key !== "_"}>
|
|
581
|
+
<span class="pw-shell__warnings-label">{formatFieldKey(key)}</span>
|
|
582
|
+
</Show>
|
|
583
|
+
{msg}
|
|
584
|
+
</li>
|
|
585
|
+
)}
|
|
586
|
+
</For>
|
|
587
|
+
</ul>
|
|
588
|
+
</Show>
|
|
589
|
+
{/* Blocking error */}
|
|
590
|
+
<Show when={showBlockingError()}>
|
|
591
|
+
<p class="pw-shell__blocking-error">{snap().blockingError}</p>
|
|
592
|
+
</Show>
|
|
593
|
+
{/* Error panel or footer */}
|
|
594
|
+
<Show
|
|
595
|
+
when={snap().status === "error" && snap().error}
|
|
596
|
+
fallback={
|
|
597
|
+
<Show when={!effectiveHideFooter()}>
|
|
598
|
+
{props.renderFooter ? (
|
|
599
|
+
props.renderFooter(snap(), actions)
|
|
600
|
+
) : (
|
|
601
|
+
<SolidFooter
|
|
541
602
|
snapshot={snap()}
|
|
542
603
|
actions={actions}
|
|
543
604
|
backLabel={props.backLabel ?? "Previous"}
|
|
@@ -548,13 +609,14 @@ export const PathShell: Component<PathShellProps> = (props) => {
|
|
|
548
609
|
hideCancel={props.hideCancel ?? false}
|
|
549
610
|
layout={resolvedFooterLayout()}
|
|
550
611
|
/>
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
612
|
+
)}
|
|
613
|
+
</Show>
|
|
614
|
+
}
|
|
615
|
+
>
|
|
616
|
+
<SolidErrorPanel snapshot={snap()} actions={actions} />
|
|
617
|
+
</Show>
|
|
556
618
|
</Show>
|
|
557
|
-
|
|
619
|
+
{/* end status !== completed */}
|
|
558
620
|
</div>
|
|
559
621
|
</Show>
|
|
560
622
|
</PathContext.Provider>
|
|
@@ -572,9 +634,7 @@ function SolidRootProgress(props: { root: RootProgress }) {
|
|
|
572
634
|
<For each={props.root.steps}>
|
|
573
635
|
{(step, i) => (
|
|
574
636
|
<div class={`pw-shell__step pw-shell__step--${step.status}`}>
|
|
575
|
-
<span class="pw-shell__step-dot">
|
|
576
|
-
{step.status === "completed" ? "✓" : String(i() + 1)}
|
|
577
|
-
</span>
|
|
637
|
+
<span class="pw-shell__step-dot">{step.status === "completed" ? "✓" : String(i() + 1)}</span>
|
|
578
638
|
<span class="pw-shell__step-label">{step.title ?? step.id}</span>
|
|
579
639
|
</div>
|
|
580
640
|
)}
|
|
@@ -598,9 +658,7 @@ function SolidHeader(props: { snapshot: PathSnapshot }) {
|
|
|
598
658
|
<For each={props.snapshot.steps}>
|
|
599
659
|
{(step, i) => (
|
|
600
660
|
<div class={`pw-shell__step pw-shell__step--${step.status}`}>
|
|
601
|
-
<span class="pw-shell__step-dot">
|
|
602
|
-
{step.status === "completed" ? "✓" : String(i() + 1)}
|
|
603
|
-
</span>
|
|
661
|
+
<span class="pw-shell__step-dot">{step.status === "completed" ? "✓" : String(i() + 1)}</span>
|
|
604
662
|
<span class="pw-shell__step-label">{step.title ?? step.id}</span>
|
|
605
663
|
</div>
|
|
606
664
|
)}
|
|
@@ -620,14 +678,15 @@ function SolidHeader(props: { snapshot: PathSnapshot }) {
|
|
|
620
678
|
function SolidErrorPanel(props: { snapshot: PathSnapshot; actions: PathShellActions }) {
|
|
621
679
|
const error = () => props.snapshot.error!;
|
|
622
680
|
const escalated = () => error().retryCount >= 2;
|
|
623
|
-
const title = () => escalated() ? "Still having trouble." : "Something went wrong.";
|
|
681
|
+
const title = () => (escalated() ? "Still having trouble." : "Something went wrong.");
|
|
624
682
|
const phaseMsg = () => errorPhaseMessage(error().phase);
|
|
625
683
|
|
|
626
684
|
return (
|
|
627
685
|
<div class="pw-shell__error">
|
|
628
686
|
<div class="pw-shell__error-title">{title()}</div>
|
|
629
687
|
<div class="pw-shell__error-message">
|
|
630
|
-
{phaseMsg()}
|
|
688
|
+
{phaseMsg()}
|
|
689
|
+
{error().message ? ` ${error().message}` : ""}
|
|
631
690
|
</div>
|
|
632
691
|
<div class="pw-shell__error-actions">
|
|
633
692
|
<Show when={!escalated()}>
|
|
@@ -740,6 +799,7 @@ export type {
|
|
|
740
799
|
PathDefinition,
|
|
741
800
|
PathEvent,
|
|
742
801
|
PathSnapshot,
|
|
802
|
+
StepStatus,
|
|
743
803
|
PathStep,
|
|
744
804
|
PathStepContext,
|
|
745
805
|
ProgressLayout,
|