@daltonr/pathwrite-svelte 0.13.0 → 0.14.0

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.
@@ -1,7 +1,7 @@
1
1
  import type { PathData, PathDefinition, PathEngine, PathEvent, PathSnapshot } from "@daltonr/pathwrite-core";
2
2
  export { formatFieldKey, errorPhaseMessage } from "@daltonr/pathwrite-core";
3
- export type { PathData, FieldErrors, PathDefinition, PathEngine, PathEvent, PathSnapshot, PathStep, PathStepContext, ProgressLayout, RootProgress, SerializedPathState } from "@daltonr/pathwrite-core";
4
- export interface UsePathOptions {
3
+ export type { PathData, FieldErrors, PathDefinition, PathEngine, PathEvent, PathSnapshot, StepStatus, PathStep, PathStepContext, ProgressLayout, RootProgress, SerializedPathState, } from "@daltonr/pathwrite-core";
4
+ export interface UsePathOptions<TData extends PathData = PathData> {
5
5
  /**
6
6
  * An externally-managed `PathEngine` to subscribe to — for example, the engine
7
7
  * returned by `restoreOrStart()` from `@daltonr/pathwrite-store`.
@@ -18,22 +18,22 @@ export interface UsePathOptions {
18
18
  * later (e.g. from an async `restoreOrStart()`) or is swapped is adopted —
19
19
  * the hook re-subscribes and re-seeds its snapshot from the new engine.
20
20
  */
21
- engine?: PathEngine;
21
+ engine?: PathEngine<TData>;
22
22
  /** Called for every engine event (stateChanged, completed, cancelled, resumed). */
23
- onEvent?: (event: PathEvent) => void;
23
+ onEvent?: (event: PathEvent<TData>) => void;
24
24
  }
25
25
  export interface UsePathReturn<TData extends PathData = PathData> {
26
26
  /**
27
- * Current path snapshot, or `null` when no path is active. Reactive via `$state`.
27
+ * Current path snapshot, or `null` when no path is active. Reactive via `$state.raw`.
28
28
  *
29
29
  * ⚠️ **Do not destructure.** `const { snapshot } = usePath()` captures the value
30
30
  * once and loses reactivity. Always access as `path.snapshot`.
31
31
  */
32
32
  readonly snapshot: PathSnapshot<TData> | null;
33
33
  /** Start (or restart) a path. */
34
- start: (path: PathDefinition<any>, initialData?: PathData) => Promise<void>;
35
- /** 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. */
36
- startSubPath: (path: PathDefinition<any>, initialData?: PathData, meta?: Record<string, unknown>) => Promise<void>;
34
+ start: (path: PathDefinition<TData>, initialData?: Partial<TData>) => Promise<void>;
35
+ /** 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. */
36
+ startSubPath: (path: PathDefinition, initialData?: PathData, meta?: Record<string, unknown>) => Promise<void>;
37
37
  /** Advance one step. Completes the path on the last step. */
38
38
  next: () => Promise<void>;
39
39
  /** 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. */
@@ -120,7 +120,7 @@ export interface UsePathReturn<TData extends PathData = PathData> {
120
120
  * {/if}
121
121
  * ```
122
122
  */
123
- export declare function usePath<TData extends PathData = PathData>(options?: UsePathOptions): UsePathReturn<TData>;
123
+ export declare function usePath<TData extends PathData = PathData>(options?: UsePathOptions<TData>): UsePathReturn<TData>;
124
124
  /**
125
125
  * Navigation actions handed to a custom `footer` snippet of `<PathShell>`
126
126
  * (`{#snippet footer(snap, actions)}`). Same shape as the other adapters'
@@ -211,9 +211,9 @@ export declare function bindData<TData extends PathData, K extends string & keyo
211
211
  };
212
212
  /**
213
213
  * Converts a hyphenated step ID to camelCase.
214
- * Used internally by PathShell to resolve step snippets when a step ID contains
215
- * hyphens (e.g. "cover-letter" → "coverLetter"), since Svelte prop names must
216
- * be valid JavaScript identifiers.
214
+ * Used internally by PathShell as a fallback key into its `steps` record, so a
215
+ * hyphenated step ID (e.g. "cover-letter") also resolves an entry registered
216
+ * under its camelCase form ("coverLetter").
217
217
  */
218
218
  export declare function stepIdToCamelCase(id: string): string;
219
219
  export { default as PathShell } from "./PathShell.svelte";
@@ -1 +1 @@
1
- {"version":3,"file":"index.svelte.d.ts","sourceRoot":"","sources":["../src/index.svelte.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.svelte.d.ts","sourceRoot":"","sources":["../src/index.svelte.ts"],"names":[],"mappings":"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"}
@@ -63,7 +63,11 @@ export function usePath(options) {
63
63
  let ownEngine = null;
64
64
  const resolveEngine = () => options?.engine ?? (ownEngine ??= new PathEngineClass());
65
65
  let engine = resolveEngine();
66
- let _snapshot = $state(engine.snapshot());
66
+ // `$state.raw`, not `$state`: snapshots are immutable values the engine
67
+ // replaces wholesale on every change and nothing mutates them in place, so a
68
+ // deep proxy over each one (and its `data`) would be pure overhead. Every
69
+ // update below reassigns the whole value, which is what `$state.raw` tracks.
70
+ let _snapshot = $state.raw(engine.snapshot());
67
71
  const onEngineEvent = (event) => {
68
72
  if (event.type === "stateChanged" || event.type === "resumed") {
69
73
  _snapshot = event.snapshot;
@@ -89,7 +93,10 @@ export function usePath(options) {
89
93
  });
90
94
  });
91
95
  // Auto-cleanup when component is destroyed
92
- onDestroy(() => { unsubscribe(); stopWatching(); });
96
+ onDestroy(() => {
97
+ unsubscribe();
98
+ stopWatching();
99
+ });
93
100
  const start = (path, initialData = {}) => engine.start(path, initialData);
94
101
  const startSubPath = (path, initialData = {}, meta) => engine.startSubPath(path, initialData, meta);
95
102
  const next = () => engine.next();
@@ -97,14 +104,16 @@ export function usePath(options) {
97
104
  const cancel = () => engine.cancel();
98
105
  const goToStep = (stepId, options) => engine.goToStep(stepId, options);
99
106
  const goToStepChecked = (stepId, options) => engine.goToStepChecked(stepId, options);
100
- const setData = ((key, value) => engine.setData(key, value));
107
+ const setData = (key, value) => engine.setData(key, value);
101
108
  const resetStep = () => engine.resetStep();
102
109
  const restart = () => engine.restart();
103
110
  const retry = () => engine.retry();
104
111
  const suspend = () => engine.suspend();
105
112
  const validate = () => engine.validate();
106
113
  return {
107
- get snapshot() { return _snapshot; },
114
+ get snapshot() {
115
+ return _snapshot;
116
+ },
108
117
  start,
109
118
  startSubPath,
110
119
  next,
@@ -117,7 +126,7 @@ export function usePath(options) {
117
126
  restart,
118
127
  retry,
119
128
  suspend,
120
- validate
129
+ validate,
121
130
  };
122
131
  }
123
132
  // ---------------------------------------------------------------------------
@@ -198,14 +207,14 @@ export function bindData(getSnapshot, setData, key) {
198
207
  },
199
208
  set(value) {
200
209
  setData(key, value);
201
- }
210
+ },
202
211
  };
203
212
  }
204
213
  /**
205
214
  * Converts a hyphenated step ID to camelCase.
206
- * Used internally by PathShell to resolve step snippets when a step ID contains
207
- * hyphens (e.g. "cover-letter" → "coverLetter"), since Svelte prop names must
208
- * be valid JavaScript identifiers.
215
+ * Used internally by PathShell as a fallback key into its `steps` record, so a
216
+ * hyphenated step ID (e.g. "cover-letter") also resolves an entry registered
217
+ * under its camelCase form ("coverLetter").
209
218
  */
210
219
  export function stepIdToCamelCase(id) {
211
220
  return id.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daltonr/pathwrite-svelte",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Svelte 5 adapter for @daltonr/pathwrite-core — runes-based reactive bindings and optional PathShell component.",
@@ -48,13 +48,13 @@
48
48
  "check": "svelte-check --tsconfig ./tsconfig.check.json --fail-on-warnings",
49
49
  "build": "npm run check && svelte-package -i src -o dist && cp ../shell.css dist/index.css",
50
50
  "clean": "rm -rf dist tsconfig.tsbuildinfo",
51
- "prepublishOnly": "test -d dist && echo 'dist already built, skipping' || (npm run clean && npm run build)"
51
+ "prepublishOnly": "npm run clean && npm run build"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "svelte": ">=5.0.0"
55
55
  },
56
56
  "dependencies": {
57
- "@daltonr/pathwrite-core": "^0.13.0"
57
+ "@daltonr/pathwrite-core": "^0.14.0"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@sveltejs/package": "^2.5.7",
@@ -1,14 +1,28 @@
1
1
  <script lang="ts">
2
- import { onMount } from 'svelte';
3
- import { usePath, setPathContext, getPathContextOrNull, formatFieldKey, errorPhaseMessage, stepIdToCamelCase } from './index.svelte.js';
4
- import type { PathDefinition, PathData, PathEngine, PathSnapshot, ProgressLayout, PathShellActions } from './index.svelte.js';
5
- import { PathEngine as PathEngineClass } from '@daltonr/pathwrite-core';
6
- import type { SerializedPathState } from '@daltonr/pathwrite-core';
7
- import type { Snippet, Component } from 'svelte';
8
-
2
+ import { onMount } from "svelte";
3
+ import {
4
+ usePath,
5
+ setPathContext,
6
+ getPathContextOrNull,
7
+ formatFieldKey,
8
+ errorPhaseMessage,
9
+ stepIdToCamelCase,
10
+ } from "./index.svelte.js";
11
+ import type {
12
+ PathDefinition,
13
+ PathData,
14
+ PathEngine,
15
+ PathEvent,
16
+ PathSnapshot,
17
+ ProgressLayout,
18
+ PathShellActions,
19
+ } from "./index.svelte.js";
20
+ import { PathEngine as PathEngineClass } from "@daltonr/pathwrite-core";
21
+ import type { SerializedPathState } from "@daltonr/pathwrite-core";
22
+ import type { Snippet, Component } from "svelte";
9
23
 
10
24
  interface Props {
11
- path?: PathDefinition<any>;
25
+ path?: PathDefinition;
12
26
  engine?: PathEngine;
13
27
  initialData?: PathData;
14
28
  /**
@@ -57,17 +71,23 @@
57
71
  * Step components access it via `usePathContext<TData, TServices>()`.
58
72
  */
59
73
  services?: unknown;
74
+ /**
75
+ * Step components keyed by step id. The shell renders the entry for the
76
+ * active step (`snapshot.formId` first, for the inner step of a StepChoice,
77
+ * then `snapshot.stepId`) with no props — step components read the path
78
+ * through `usePathContext()`. Hyphenated ids may be given as-is
79
+ * (`"cover-letter"`) or in camelCase (`coverLetter`).
80
+ */
81
+ steps?: Record<string, Component>;
60
82
  // Callback props replace event dispatching in Svelte 5
61
83
  oncomplete?: (data: PathData) => void;
62
84
  oncancel?: (data: PathData) => void;
63
- onevent?: (event: any) => void;
85
+ onevent?: (event: PathEvent) => void;
64
86
  // Optional override snippets for header and footer
65
- header?: Snippet<[PathSnapshot<any>]>;
66
- footer?: Snippet<[PathSnapshot<any>, PathShellActions]>;
87
+ header?: Snippet<[PathSnapshot]>;
88
+ footer?: Snippet<[PathSnapshot, PathShellActions]>;
67
89
  /** Snippet rendered when `snapshot.status === "completed"`. Defaults to a simple "All done." panel with a restart button. */
68
- completion?: Snippet<[PathSnapshot<any>]>;
69
- // All other props treated as step components keyed by step ID
70
- [key: string]: Component<any> | any;
90
+ completion?: Snippet<[PathSnapshot]>;
71
91
  }
72
92
 
73
93
  let {
@@ -76,26 +96,29 @@
76
96
  initialData = {},
77
97
  restoreKey = undefined,
78
98
  autoStart = true,
79
- backLabel = 'Previous',
80
- nextLabel = 'Next',
81
- completeLabel = 'Complete',
99
+ backLabel = "Previous",
100
+ nextLabel = "Next",
101
+ completeLabel = "Complete",
82
102
  loadingLabel = undefined,
83
- cancelLabel = 'Cancel',
103
+ cancelLabel = "Cancel",
84
104
  hideCancel = false,
85
105
  hideProgress = false,
86
106
  hideFooter = false,
87
107
  validateWhen = false,
88
- layout = 'auto',
89
- validationDisplay = 'summary',
90
- progressLayout = 'merged',
108
+ layout = "auto",
109
+ validationDisplay = "summary",
110
+ progressLayout = "merged",
91
111
  services = null,
112
+ steps = {},
92
113
  oncomplete,
93
114
  oncancel,
94
115
  onevent,
95
116
  header,
96
117
  footer,
97
118
  completion,
98
- ...stepSnippets
119
+ // Not part of `Props` (which has no index signature, so a stray prop is a
120
+ // type error): kept only for the runtime camelCase-callback warning below.
121
+ ...rest
99
122
  }: Props = $props();
100
123
 
101
124
  // Read outer PathShell context BEFORE setting our own — gives access to
@@ -108,8 +131,9 @@
108
131
  // svelte-ignore state_referenced_locally — read once at init on purpose: restore happens at mount only
109
132
  const restoredEngine: PathEngine | null = (() => {
110
133
  if (engineProp || !restoreKey || !outerCtx || !path) return null;
111
- const stored = outerCtx.snapshot?.data[restoreKey] as { serializedState?: SerializedPathState } | undefined;
112
- if (!stored || typeof stored !== 'object' || !stored.serializedState) return null;
134
+ const stored = outerCtx.snapshot?.data[restoreKey] as
135
+ { serializedState?: SerializedPathState } | undefined;
136
+ if (!stored || typeof stored !== "object" || !stored.serializedState) return null;
113
137
  try {
114
138
  return PathEngineClass.fromState(stored.serializedState, { [path.id]: path });
115
139
  } catch {
@@ -123,24 +147,43 @@
123
147
 
124
148
  // Initialize path engine
125
149
  const pathReturn = usePath({
126
- get engine() { return currentEngine(); },
150
+ get engine() {
151
+ return currentEngine();
152
+ },
127
153
  onEvent: (event) => {
128
154
  onevent?.(event);
129
- if (event.type === 'completed') oncomplete?.(event.data);
130
- if (event.type === 'cancelled') oncancel?.(event.data);
131
- if (restoreKey && outerCtx && event.type === 'stateChanged') {
132
- (outerCtx.setData as unknown as (key: string, value: unknown) => Promise<void>)(
133
- restoreKey, { ...event.snapshot, serializedState: currentEngine().exportState() }
134
- );
155
+ if (event.type === "completed") oncomplete?.(event.data);
156
+ if (event.type === "cancelled") oncancel?.(event.data);
157
+ if (restoreKey && outerCtx && event.type === "stateChanged") {
158
+ void outerCtx.setData(restoreKey, {
159
+ ...event.snapshot,
160
+ serializedState: currentEngine().exportState(),
161
+ });
135
162
  }
136
- }
163
+ },
137
164
  });
138
165
 
139
- const { start, startSubPath, next, previous, cancel, goToStep, goToStepChecked, setData, resetStep, restart: restartFn, retry, suspend, validate } = pathReturn;
166
+ const {
167
+ start,
168
+ startSubPath,
169
+ next,
170
+ previous,
171
+ cancel,
172
+ goToStep,
173
+ goToStepChecked,
174
+ setData,
175
+ resetStep,
176
+ restart: restartFn,
177
+ retry,
178
+ suspend,
179
+ validate,
180
+ } = pathReturn;
140
181
 
141
182
  // Provide context for child step components
142
183
  setPathContext({
143
- get snapshot() { return pathReturn.snapshot; },
184
+ get snapshot() {
185
+ return pathReturn.snapshot;
186
+ },
144
187
  start,
145
188
  startSubPath,
146
189
  validate,
@@ -154,21 +197,25 @@
154
197
  restart: () => restartFn(),
155
198
  retry,
156
199
  suspend,
157
- get services() { return services; },
200
+ get services() {
201
+ return services;
202
+ },
158
203
  });
159
204
 
160
205
  // Dev-mode warning: camelCase callback props are silently ignored in Svelte.
161
206
  // Warn if the user passed onComplete/onCancel/onEvent instead of the correct
162
- // lowercase forms oncomplete/oncancel/onevent. Runs once, on mount (a closure
163
- // reading the props at the top level would only capture their initial value).
207
+ // lowercase forms oncomplete/oncancel/onevent (a type error in TypeScript,
208
+ // but plain JavaScript callers get no such hint). Runs once, on mount (a
209
+ // closure — reading the props at the top level would only capture their
210
+ // initial value).
164
211
  // `import.meta.env` is a bundler convention (Vite); the cast keeps this
165
212
  // package free of Vite's ambient types.
166
213
  const isDev = (import.meta as { env?: { DEV?: boolean } }).env?.DEV !== false;
167
214
  onMount(() => {
168
215
  if (!isDev) return;
169
- const camelCallbacks = ['onComplete', 'onCancel', 'onEvent'] as const;
216
+ const camelCallbacks = ["onComplete", "onCancel", "onEvent"] as const;
170
217
  for (const name of camelCallbacks) {
171
- if (name in stepSnippets) {
218
+ if (name in rest) {
172
219
  console.warn(
173
220
  `[PathShell] "${name}" was passed but will be ignored. Svelte uses lowercase callback props — use "${name.toLowerCase()}" instead.`
174
221
  );
@@ -185,8 +232,8 @@
185
232
  let startData: PathData = initialData ?? {};
186
233
  let restoreStepId: string | undefined;
187
234
  if (restoreKey && outerCtx) {
188
- const stored = outerCtx.snapshot?.data[restoreKey] as PathSnapshot<any> | undefined;
189
- if (stored != null && typeof stored === 'object' && 'stepId' in stored) {
235
+ const stored = outerCtx.snapshot?.data[restoreKey] as PathSnapshot | undefined;
236
+ if (stored != null && typeof stored === "object" && "stepId" in stored) {
190
237
  startData = stored.data as PathData;
191
238
  if (stored.stepIndex > 0) restoreStepId = stored.stepId as string;
192
239
  }
@@ -204,27 +251,38 @@
204
251
 
205
252
  function warnMissingStep(stepId: string): void {
206
253
  const camel = stepIdToCamelCase(stepId);
207
- const hint = camel !== stepId
208
- ? ` No snippet found for "${stepId}" or its camelCase form "${camel}". If your step ID contains hyphens, pass the snippet as a camelCase prop: ${camel}={YourComponent}.`
209
- : ` No snippet found for "${stepId}".`;
254
+ const hint =
255
+ camel !== stepId
256
+ ? ` No step component found for "${stepId}" or its camelCase form "${camel}". Pass it in the \`steps\` record under either key.`
257
+ : ` No step component found for "${stepId}". Pass it in the \`steps\` record.`;
210
258
  console.warn(`[PathShell]${hint}`);
211
259
  }
212
260
 
213
261
  let snap = $derived(pathReturn.snapshot);
214
262
  let actions: PathShellActions = $derived({
215
- next, previous, cancel, goToStep, goToStepChecked,
216
- setData: (key, value) => setData(key as never, value as never),
217
- restart: () => restartFn(), retry, suspend
263
+ next,
264
+ previous,
265
+ cancel,
266
+ goToStep,
267
+ goToStepChecked,
268
+ setData,
269
+ restart: () => restartFn(),
270
+ retry,
271
+ suspend,
218
272
  });
219
273
 
220
- let effectiveHideProgress = $derived(hideProgress || layout === 'tabs');
221
- let effectiveHideFooter = $derived(hideFooter || layout === 'tabs');
274
+ let effectiveHideProgress = $derived(hideProgress || layout === "tabs");
275
+ let effectiveHideFooter = $derived(hideFooter || layout === "tabs");
222
276
 
223
277
  // Auto-detect footer layout: single-step top-level paths use "form", everything else uses "wizard"
224
278
  let resolvedFooterLayout = $derived(
225
- (layout === 'auto' || layout === 'tabs') && snap
226
- ? (snap.stepCount === 1 && snap.nestingLevel === 0 ? 'form' : 'wizard')
227
- : (layout === 'auto' || layout === 'tabs' ? 'wizard' : layout)
279
+ (layout === "auto" || layout === "tabs") && snap
280
+ ? snap.stepCount === 1 && snap.nestingLevel === 0
281
+ ? "form"
282
+ : "wizard"
283
+ : layout === "auto" || layout === "tabs"
284
+ ? "wizard"
285
+ : layout
228
286
  );
229
287
 
230
288
  /**
@@ -251,7 +309,7 @@
251
309
  </button>
252
310
  {/if}
253
311
  </div>
254
- {:else if snap.status === 'completed'}
312
+ {:else if snap.status === "completed"}
255
313
  <!-- Completion panel: shown after stayOnFinal completion -->
256
314
  {#if !effectiveHideProgress && snap.stepCount > 1}
257
315
  <div class="pw-shell__header">
@@ -282,13 +340,13 @@
282
340
  </div>
283
341
  {:else}
284
342
  <!-- Root progress: persistent top-level bar visible during sub-paths -->
285
- {#if !effectiveHideProgress && snap.rootProgress && progressLayout !== 'activeOnly'}
343
+ {#if !effectiveHideProgress && snap.rootProgress && progressLayout !== "activeOnly"}
286
344
  <div class="pw-shell__root-progress">
287
345
  <div class="pw-shell__steps">
288
346
  {#each snap.rootProgress.steps as step, i}
289
347
  <div class="pw-shell__step pw-shell__step--{step.status}">
290
348
  <span class="pw-shell__step-dot">
291
- {step.status === 'completed' ? '' : i + 1}
349
+ {step.status === "completed" ? "" : i + 1}
292
350
  </span>
293
351
  <span class="pw-shell__step-label">{step.title ?? step.id}</span>
294
352
  </div>
@@ -301,7 +359,7 @@
301
359
  {/if}
302
360
 
303
361
  <!-- Header: progress indicator (overridable via header snippet) -->
304
- {#if !effectiveHideProgress && progressLayout !== 'rootOnly'}
362
+ {#if !effectiveHideProgress && progressLayout !== "rootOnly"}
305
363
  {#if header}
306
364
  {@render header(snap)}
307
365
  {:else if snap.stepCount > 1 || snap.nestingLevel > 0}
@@ -310,7 +368,7 @@
310
368
  {#each snap.steps as step, i}
311
369
  <div class="pw-shell__step pw-shell__step--{step.status}">
312
370
  <span class="pw-shell__step-dot">
313
- {step.status === 'completed' ? '' : i + 1}
371
+ {step.status === "completed" ? "" : i + 1}
314
372
  </span>
315
373
  <span class="pw-shell__step-label">{step.title ?? step.id}</span>
316
374
  </div>
@@ -323,21 +381,20 @@
323
381
  {/if}
324
382
  {/if}
325
383
 
326
- <!-- Body: current step rendered via named snippet.
384
+ <!-- Body: the active step's component from the `steps` record.
327
385
  Prefer formId (inner step id of a StepChoice) so consumers can
328
- register snippets by inner step ids directly.
329
- Hyphenated step IDs (e.g. "cover-letter") are normalised to camelCase
330
- ("coverLetter") as a fallback, since Svelte props must be valid JS
331
- identifiers. -->
386
+ register components by inner step ids directly.
387
+ Hyphenated step IDs (e.g. "cover-letter") also resolve under their
388
+ camelCase form ("coverLetter"). -->
332
389
  <div class="pw-shell__body">
333
- {#if snap.formId && stepSnippets[snap.formId]}
334
- {@const StepComponent = stepSnippets[snap.formId]}
390
+ {#if snap.formId && steps[snap.formId]}
391
+ {@const StepComponent = steps[snap.formId]}
335
392
  <StepComponent />
336
- {:else if stepSnippets[snap.stepId]}
337
- {@const StepComponent = stepSnippets[snap.stepId]}
393
+ {:else if steps[snap.stepId]}
394
+ {@const StepComponent = steps[snap.stepId]}
338
395
  <StepComponent />
339
- {:else if stepSnippets[stepIdToCamelCase(snap.formId ?? snap.stepId)]}
340
- {@const StepComponent = stepSnippets[stepIdToCamelCase(snap.formId ?? snap.stepId)]}
396
+ {:else if steps[stepIdToCamelCase(snap.formId ?? snap.stepId)]}
397
+ {@const StepComponent = steps[stepIdToCamelCase(snap.formId ?? snap.stepId)]}
341
398
  <StepComponent />
342
399
  {:else}
343
400
  {warnMissingStep(snap.stepId)}
@@ -346,29 +403,29 @@
346
403
  </div>
347
404
 
348
405
  <!-- Validation messages — suppressed when validationDisplay="inline" -->
349
- {#if validationDisplay !== 'inline' && (snap.hasAttemptedNext || snap.hasValidated) && Object.keys(snap.fieldErrors).length > 0}
406
+ {#if validationDisplay !== "inline" && (snap.hasAttemptedNext || snap.hasValidated) && Object.keys(snap.fieldErrors).length > 0}
350
407
  <ul class="pw-shell__validation">
351
408
  {#each Object.entries(snap.fieldErrors) as [key, msg]}
352
409
  <li class="pw-shell__validation-item">
353
- {#if key !== '_'}<span class="pw-shell__validation-label">{formatFieldKey(key)}</span>{/if}{msg}
410
+ {#if key !== "_"}<span class="pw-shell__validation-label">{formatFieldKey(key)}</span>{/if}{msg}
354
411
  </li>
355
412
  {/each}
356
413
  </ul>
357
414
  {/if}
358
415
 
359
416
  <!-- Warning messages — non-blocking, shown immediately (no hasAttemptedNext gate) -->
360
- {#if validationDisplay !== 'inline' && Object.keys(snap.fieldWarnings).length > 0}
417
+ {#if validationDisplay !== "inline" && Object.keys(snap.fieldWarnings).length > 0}
361
418
  <ul class="pw-shell__warnings">
362
419
  {#each Object.entries(snap.fieldWarnings) as [key, msg]}
363
420
  <li class="pw-shell__warnings-item">
364
- {#if key !== '_'}<span class="pw-shell__warnings-label">{formatFieldKey(key)}</span>{/if}{msg}
421
+ {#if key !== "_"}<span class="pw-shell__warnings-label">{formatFieldKey(key)}</span>{/if}{msg}
365
422
  </li>
366
423
  {/each}
367
424
  </ul>
368
425
  {/if}
369
426
 
370
427
  <!-- Blocking error — guard returned { allowed: false, reason } -->
371
- {#if validationDisplay !== 'inline' && (snap.hasAttemptedNext || snap.hasValidated) && snap.blockingError}
428
+ {#if validationDisplay !== "inline" && (snap.hasAttemptedNext || snap.hasValidated) && snap.blockingError}
372
429
  <p class="pw-shell__blocking-error">{snap.blockingError}</p>
373
430
  {/if}
374
431
 
@@ -377,31 +434,37 @@
377
434
  {@const err = snap.error}
378
435
  {@const escalated = err.retryCount >= 2}
379
436
  <div class="pw-shell__error">
380
- <div class="pw-shell__error-title">{escalated ? "Still having trouble." : "Something went wrong."}</div>
381
- <div class="pw-shell__error-message">{errorPhaseMessage(err.phase)}{err.message ? ` ${err.message}` : ""}</div>
437
+ <div class="pw-shell__error-title">
438
+ {escalated ? "Still having trouble." : "Something went wrong."}
439
+ </div>
440
+ <div class="pw-shell__error-message">
441
+ {errorPhaseMessage(err.phase)}{err.message ? ` ${err.message}` : ""}
442
+ </div>
382
443
  <div class="pw-shell__error-actions">
383
444
  {#if !escalated}
384
- <button type="button" class="pw-shell__btn pw-shell__btn--retry" onclick={retry}>Try again</button>
445
+ <button type="button" class="pw-shell__btn pw-shell__btn--retry" onclick={retry}>Try again</button
446
+ >
385
447
  {/if}
386
448
  {#if snap.hasPersistence}
387
449
  <button
388
450
  type="button"
389
451
  class="pw-shell__btn {escalated ? 'pw-shell__btn--retry' : 'pw-shell__btn--suspend'}"
390
- onclick={suspend}
391
- >Save and come back later</button>
452
+ onclick={suspend}>Save and come back later</button
453
+ >
392
454
  {/if}
393
455
  {#if escalated && !snap.hasPersistence}
394
- <button type="button" class="pw-shell__btn pw-shell__btn--retry" onclick={retry}>Try again</button>
456
+ <button type="button" class="pw-shell__btn pw-shell__btn--retry" onclick={retry}>Try again</button
457
+ >
395
458
  {/if}
396
459
  </div>
397
460
  </div>
398
- <!-- Footer: navigation buttons (overridable via footer snippet) -->
461
+ <!-- Footer: navigation buttons (overridable via footer snippet) -->
399
462
  {:else if !effectiveHideFooter && footer}
400
463
  {@render footer(snap, actions)}
401
464
  {:else if !effectiveHideFooter}
402
465
  <div class="pw-shell__footer">
403
466
  <div class="pw-shell__footer-left">
404
- {#if resolvedFooterLayout === 'form' && !hideCancel}
467
+ {#if resolvedFooterLayout === "form" && !hideCancel}
405
468
  <!-- Form mode: Cancel on the left -->
406
469
  <button
407
470
  type="button"
@@ -411,7 +474,7 @@
411
474
  >
412
475
  {cancelLabel}
413
476
  </button>
414
- {:else if resolvedFooterLayout === 'wizard' && !snap.isFirstStep}
477
+ {:else if resolvedFooterLayout === "wizard" && !snap.isFirstStep}
415
478
  <!-- Wizard mode: Back on the left -->
416
479
  <button
417
480
  type="button"
@@ -424,7 +487,7 @@
424
487
  {/if}
425
488
  </div>
426
489
  <div class="pw-shell__footer-right">
427
- {#if resolvedFooterLayout === 'wizard' && !hideCancel}
490
+ {#if resolvedFooterLayout === "wizard" && !hideCancel}
428
491
  <!-- Wizard mode: Cancel on the right -->
429
492
  <button
430
493
  type="button"
@@ -443,7 +506,11 @@
443
506
  disabled={snap.status !== "idle"}
444
507
  onclick={next}
445
508
  >
446
- {snap.status !== 'idle' && loadingLabel ? loadingLabel : snap.isLastStep ? completeLabel : nextLabel}
509
+ {snap.status !== "idle" && loadingLabel
510
+ ? loadingLabel
511
+ : snap.isLastStep
512
+ ? completeLabel
513
+ : nextLabel}
447
514
  </button>
448
515
  </div>
449
516
  </div>