aeon-ui-engine 1.1.0 → 1.2.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.
Files changed (33) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/CONSUMER.md +45 -0
  3. package/package.json +1 -1
  4. package/packages/core/dist/index.d.ts +88 -8
  5. package/packages/core/dist/index.js +113 -0
  6. package/packages/core/src/anatomy.ts +38 -0
  7. package/packages/core/src/brand-theme.ts +172 -0
  8. package/packages/core/src/index.ts +1 -0
  9. package/packages/panda/aeon.css +1 -0
  10. package/packages/panda/src/theme/consumer-shell.css +129 -0
  11. package/packages/panda/src/theme/electron.css +30 -0
  12. package/packages/primitives/dist/chunk-4NOKRUSG.js +81 -0
  13. package/packages/primitives/dist/index.d.ts +1 -0
  14. package/packages/primitives/dist/index.js +16 -6
  15. package/packages/primitives/dist/machines/app-nav.d.ts +96 -0
  16. package/packages/primitives/dist/machines/app-nav.js +12 -0
  17. package/packages/primitives/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  18. package/packages/primitives/src/__tests__/app-nav.test.ts +30 -0
  19. package/packages/primitives/src/index.ts +1 -0
  20. package/packages/primitives/src/machines/app-nav.ts +107 -0
  21. package/packages/react/dist/index.d.ts +135 -3
  22. package/packages/react/dist/index.js +1126 -720
  23. package/packages/react/src/app-nav/index.tsx +249 -0
  24. package/packages/react/src/avatar/index.tsx +4 -1
  25. package/packages/react/src/hooks/use-fit-text.ts +59 -0
  26. package/packages/react/src/index.ts +4 -0
  27. package/packages/react/src/prompt/index.tsx +161 -0
  28. package/packages/react/src/status-banner/index.tsx +84 -0
  29. package/packages/ui/src/app-nav.tsx +2 -0
  30. package/packages/ui/src/index.ts +4 -0
  31. package/packages/ui/src/prompt.tsx +29 -0
  32. package/packages/ui/src/status-banner.tsx +1 -0
  33. package/scripts/vite-aliases.mjs +8 -0
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Consumer shell recipes — baseline look for Prompt, StatusBanner, AppNav chrome.
3
+ * Opt into brand color via applyBrandPalette(); these rules only use Aeon CSS vars.
4
+ * Intentionally restrained — products still own marketing atmosphere.
5
+ */
6
+
7
+ [data-aeon-scope='prompt'][data-aeon-part='root'] {
8
+ display: contents;
9
+ }
10
+
11
+ [data-aeon-scope='prompt'][data-aeon-part='eyebrow'] {
12
+ margin: 0 0 8px;
13
+ color: var(--colors-aeon-accent);
14
+ font-size: 0.72rem;
15
+ letter-spacing: 0.14em;
16
+ text-transform: uppercase;
17
+ }
18
+
19
+ [data-aeon-scope='prompt'][data-aeon-part='meta'] {
20
+ display: grid;
21
+ gap: 10px;
22
+ margin: 16px 0;
23
+ padding: 14px 16px;
24
+ border-radius: var(--aeon-radius, 12px);
25
+ background: var(--colors-aeon-accent-dim);
26
+ }
27
+
28
+ [data-aeon-scope='prompt'][data-aeon-part='amount'] {
29
+ font-size: 1.35rem;
30
+ font-weight: 700;
31
+ color: var(--colors-aeon-text);
32
+ }
33
+
34
+ [data-aeon-scope='prompt'][data-aeon-part='actions'] {
35
+ display: flex;
36
+ justify-content: flex-end;
37
+ gap: 10px;
38
+ margin-top: 18px;
39
+ }
40
+
41
+ [data-aeon-scope='prompt'][data-aeon-part='primary'],
42
+ [data-aeon-scope='prompt'][data-aeon-part='secondary'] {
43
+ font: inherit;
44
+ cursor: pointer;
45
+ border-radius: var(--aeon-radius, 10px);
46
+ padding: 10px 14px;
47
+ border: 1px solid transparent;
48
+ }
49
+
50
+ [data-aeon-scope='prompt'][data-aeon-part='primary'] {
51
+ background: var(--colors-aeon-accent);
52
+ color: var(--colors-aeon-bg);
53
+ font-weight: 700;
54
+ }
55
+
56
+ [data-aeon-scope='prompt'][data-aeon-part='secondary'] {
57
+ background: transparent;
58
+ color: var(--colors-aeon-muted);
59
+ border-color: var(--colors-aeon-border);
60
+ }
61
+
62
+ [data-aeon-scope='statusBanner'][data-aeon-part='root'] {
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: space-between;
66
+ gap: 12px;
67
+ padding: 10px 14px;
68
+ background: color-mix(in srgb, var(--colors-aeon-accent) 14%, var(--colors-aeon-bg));
69
+ border-bottom: 1px solid color-mix(in srgb, var(--colors-aeon-accent) 35%, transparent);
70
+ }
71
+
72
+ [data-aeon-scope='statusBanner'][data-aeon-part='copy'] {
73
+ display: grid;
74
+ gap: 2px;
75
+ min-width: 0;
76
+ }
77
+
78
+ [data-aeon-scope='statusBanner'][data-aeon-part='title'] {
79
+ font-size: 0.9rem;
80
+ color: var(--colors-aeon-text);
81
+ }
82
+
83
+ [data-aeon-scope='statusBanner'][data-aeon-part='body'] {
84
+ font-size: 0.78rem;
85
+ line-height: 1.35;
86
+ color: var(--colors-aeon-muted);
87
+ }
88
+
89
+ [data-aeon-scope='statusBanner'][data-aeon-part='actions'] {
90
+ flex-shrink: 0;
91
+ }
92
+
93
+ [data-aeon-scope='appNav'][data-aeon-part='breadcrumb'] {
94
+ display: flex;
95
+ flex-wrap: wrap;
96
+ gap: 6px;
97
+ align-items: center;
98
+ }
99
+
100
+ [data-aeon-scope='appNav'][data-aeon-part='crumb'] {
101
+ font: inherit;
102
+ background: transparent;
103
+ border: 0;
104
+ color: var(--colors-aeon-muted);
105
+ cursor: pointer;
106
+ padding: 0;
107
+ }
108
+
109
+ [data-aeon-scope='appNav'][data-aeon-part='crumb'][data-aeon-state~='current'] {
110
+ color: var(--colors-aeon-text);
111
+ font-weight: 600;
112
+ cursor: default;
113
+ }
114
+
115
+ [data-aeon-scope='appNav'][data-aeon-part='crumb']:not(:last-child)::after {
116
+ content: '/';
117
+ margin-left: 6px;
118
+ color: var(--colors-aeon-border);
119
+ pointer-events: none;
120
+ }
121
+
122
+ [data-aeon-scope='appNav'][data-aeon-part='back'] {
123
+ font: inherit;
124
+ background: transparent;
125
+ border: 0;
126
+ color: var(--colors-aeon-accent);
127
+ cursor: pointer;
128
+ padding: 0;
129
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Optional Electron chrome helpers.
3
+ * Import only in desktop shells: import '@aeon-ui/panda/electron.css'
4
+ */
5
+
6
+ .aeon-titlebar {
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: space-between;
10
+ padding: 12px 20px;
11
+ border-bottom: 1px solid var(--colors-aeon-border);
12
+ background: color-mix(in srgb, var(--colors-aeon-bg) 88%, transparent);
13
+ backdrop-filter: blur(12px);
14
+ -webkit-app-region: drag;
15
+ app-region: drag;
16
+ }
17
+
18
+ .aeon-titlebar button,
19
+ .aeon-titlebar a,
20
+ .aeon-titlebar input,
21
+ .aeon-titlebar [data-aeon-no-drag] {
22
+ -webkit-app-region: no-drag;
23
+ app-region: no-drag;
24
+ }
25
+
26
+ html.platform-darwin .aeon-titlebar,
27
+ html[data-aeon-platform='darwin'] .aeon-titlebar {
28
+ /* hiddenInset traffic lights */
29
+ padding-top: 36px;
30
+ }
@@ -0,0 +1,81 @@
1
+ // src/machines/app-nav.ts
2
+ import { assign, setup } from "xstate";
3
+ function appNavDepth(ctx) {
4
+ return ctx.stack.length;
5
+ }
6
+ function appNavChild(ctx) {
7
+ return ctx.stack.length ? ctx.stack[ctx.stack.length - 1] : null;
8
+ }
9
+ function appNavStateAttr(ctx) {
10
+ const child = appNavChild(ctx);
11
+ return child ? `${ctx.section}/${child.type}` : ctx.section;
12
+ }
13
+ var appNavMachine = setup({
14
+ types: {
15
+ context: {},
16
+ events: {},
17
+ input: {}
18
+ },
19
+ actions: {
20
+ setSection: assign({
21
+ section: ({ event }) => event.type === "SET_SECTION" ? event.section : "",
22
+ stack: []
23
+ }),
24
+ push: assign({
25
+ stack: ({ context, event }) => event.type === "PUSH" ? [...context.stack, event.child] : context.stack
26
+ }),
27
+ replace: assign({
28
+ stack: ({ context, event }) => {
29
+ if (event.type !== "REPLACE") return context.stack;
30
+ if (!context.stack.length) return [event.child];
31
+ return [...context.stack.slice(0, -1), event.child];
32
+ }
33
+ }),
34
+ pop: assign({
35
+ stack: ({ context }) => context.stack.slice(0, -1)
36
+ }),
37
+ popTo: assign({
38
+ stack: ({ context, event }) => {
39
+ if (event.type !== "POP_TO") return context.stack;
40
+ const idx = context.stack.findIndex((c) => c.id === event.id);
41
+ if (idx < 0) return context.stack;
42
+ return context.stack.slice(0, idx + 1);
43
+ }
44
+ }),
45
+ clear: assign({ stack: [] }),
46
+ reset: assign({
47
+ section: ({ event }) => event.type === "RESET" ? event.section : "",
48
+ stack: []
49
+ })
50
+ },
51
+ guards: {
52
+ canPop: ({ context }) => context.stack.length > 0
53
+ }
54
+ }).createMachine({
55
+ id: "appNav",
56
+ context: ({ input }) => ({
57
+ section: input?.section ?? "home",
58
+ stack: input?.stack ?? []
59
+ }),
60
+ initial: "ready",
61
+ states: {
62
+ ready: {
63
+ on: {
64
+ SET_SECTION: { actions: "setSection" },
65
+ PUSH: { actions: "push" },
66
+ REPLACE: { actions: "replace" },
67
+ POP: { guard: "canPop", actions: "pop" },
68
+ POP_TO: { actions: "popTo" },
69
+ CLEAR: { actions: "clear" },
70
+ RESET: { actions: "reset" }
71
+ }
72
+ }
73
+ }
74
+ });
75
+
76
+ export {
77
+ appNavDepth,
78
+ appNavChild,
79
+ appNavStateAttr,
80
+ appNavMachine
81
+ };
@@ -10,6 +10,7 @@ export { PopoverContext, PopoverEvent, popoverMachine } from './machines/popover
10
10
  export { ToastContext, ToastEvent, toastMachine } from './machines/toast.js';
11
11
  export { SliderContext, SliderEvent, clampSliderValue, sliderMachine, snapSliderValue } from './machines/slider.js';
12
12
  export { APP_SHELL_STATES, AppShellContext, AppShellEvent, AppShellState, appShellMachine } from './machines/app-shell.js';
13
+ export { AppNavChild, AppNavContext, AppNavEvent, appNavChild, appNavDepth, appNavMachine, appNavStateAttr } from './machines/app-nav.js';
13
14
  export { STICKY_BAR_STATES, StickyBarContext, StickyBarEvent, StickyBarState, stickyBarMachine } from './machines/sticky-bar.js';
14
15
  export { CONTENT_REGION_STATES, ContentRegionContext, ContentRegionEvent, ContentRegionState, contentRegionMachine } from './machines/content-region.js';
15
16
  import 'xstate';
@@ -1,3 +1,9 @@
1
+ import {
2
+ FIELD_INTERACTION_STATES,
3
+ FIELD_SUBMISSION_STATES,
4
+ FIELD_VALIDATION_STATES,
5
+ fieldMachine
6
+ } from "./chunk-ZMGRX64G.js";
1
7
  import {
2
8
  panelMachine
3
9
  } from "./chunk-V5D7LS4J.js";
@@ -25,6 +31,12 @@ import {
25
31
  import {
26
32
  accordionMachine
27
33
  } from "./chunk-JBAG5E6W.js";
34
+ import {
35
+ appNavChild,
36
+ appNavDepth,
37
+ appNavMachine,
38
+ appNavStateAttr
39
+ } from "./chunk-4NOKRUSG.js";
28
40
  import {
29
41
  APP_SHELL_STATES,
30
42
  appShellMachine
@@ -46,12 +58,6 @@ import {
46
58
  import {
47
59
  dialogMachine
48
60
  } from "./chunk-SZV6KGXN.js";
49
- import {
50
- FIELD_INTERACTION_STATES,
51
- FIELD_SUBMISSION_STATES,
52
- FIELD_VALIDATION_STATES,
53
- fieldMachine
54
- } from "./chunk-ZMGRX64G.js";
55
61
  export {
56
62
  APP_SHELL_STATES,
57
63
  ASYNC_LIFECYCLE_STATES,
@@ -62,6 +68,10 @@ export {
62
68
  FIELD_VALIDATION_STATES,
63
69
  STICKY_BAR_STATES,
64
70
  accordionMachine,
71
+ appNavChild,
72
+ appNavDepth,
73
+ appNavMachine,
74
+ appNavStateAttr,
65
75
  appShellMachine,
66
76
  asyncMachine,
67
77
  buttonLifecycleMachine,
@@ -0,0 +1,96 @@
1
+ import * as xstate from 'xstate';
2
+
3
+ /**
4
+ * Section + detail stack navigation — the pattern every multi-panel product
5
+ * reinvented (wallet sections, settings drill-in, list→detail).
6
+ *
7
+ * Not product-specific: `section` is a string id; `stack` is opaque child records.
8
+ */
9
+ type AppNavChild = {
10
+ id: string;
11
+ /** Free-form type tag (e.g. "detail", "compose", "settings"). */
12
+ type: string;
13
+ /** Optional payload for the view layer. */
14
+ data?: unknown;
15
+ };
16
+ interface AppNavContext {
17
+ section: string;
18
+ stack: AppNavChild[];
19
+ }
20
+ type AppNavEvent = {
21
+ type: 'SET_SECTION';
22
+ section: string;
23
+ } | {
24
+ type: 'PUSH';
25
+ child: AppNavChild;
26
+ } | {
27
+ type: 'REPLACE';
28
+ child: AppNavChild;
29
+ } | {
30
+ type: 'POP';
31
+ } | {
32
+ type: 'POP_TO';
33
+ id: string;
34
+ } | {
35
+ type: 'CLEAR';
36
+ } | {
37
+ type: 'RESET';
38
+ section: string;
39
+ };
40
+ declare function appNavDepth(ctx: AppNavContext): number;
41
+ declare function appNavChild(ctx: AppNavContext): AppNavChild | null;
42
+ /** Projected state for data-aeon-state: `section` or `section/childType`. */
43
+ declare function appNavStateAttr(ctx: AppNavContext): string;
44
+ declare const appNavMachine: xstate.StateMachine<AppNavContext, {
45
+ type: "SET_SECTION";
46
+ section: string;
47
+ } | {
48
+ type: "PUSH";
49
+ child: AppNavChild;
50
+ } | {
51
+ type: "REPLACE";
52
+ child: AppNavChild;
53
+ } | {
54
+ type: "POP";
55
+ } | {
56
+ type: "POP_TO";
57
+ id: string;
58
+ } | {
59
+ type: "CLEAR";
60
+ } | {
61
+ type: "RESET";
62
+ section: string;
63
+ }, {}, never, {
64
+ type: "replace";
65
+ params: xstate.NonReducibleUnknown;
66
+ } | {
67
+ type: "pop";
68
+ params: xstate.NonReducibleUnknown;
69
+ } | {
70
+ type: "push";
71
+ params: xstate.NonReducibleUnknown;
72
+ } | {
73
+ type: "clear";
74
+ params: xstate.NonReducibleUnknown;
75
+ } | {
76
+ type: "setSection";
77
+ params: xstate.NonReducibleUnknown;
78
+ } | {
79
+ type: "popTo";
80
+ params: xstate.NonReducibleUnknown;
81
+ } | {
82
+ type: "reset";
83
+ params: xstate.NonReducibleUnknown;
84
+ }, {
85
+ type: "canPop";
86
+ params: unknown;
87
+ }, never, "ready", string, Partial<AppNavContext> & {
88
+ section?: string;
89
+ }, xstate.NonReducibleUnknown, xstate.EventObject, xstate.MetaObject, {
90
+ id: "appNav";
91
+ states: {
92
+ readonly ready: {};
93
+ };
94
+ }>;
95
+
96
+ export { type AppNavChild, type AppNavContext, type AppNavEvent, appNavChild, appNavDepth, appNavMachine, appNavStateAttr };
@@ -0,0 +1,12 @@
1
+ import {
2
+ appNavChild,
3
+ appNavDepth,
4
+ appNavMachine,
5
+ appNavStateAttr
6
+ } from "../chunk-4NOKRUSG.js";
7
+ export {
8
+ appNavChild,
9
+ appNavDepth,
10
+ appNavMachine,
11
+ appNavStateAttr
12
+ };
@@ -1 +1 @@
1
- {"version":"3.2.4","results":[[":src/machines/dialog.test.ts",{"duration":5.771292000000017,"failed":false}],[":src/machines/select.test.ts",{"duration":6.394416000000035,"failed":false}],[":src/__tests__/dialog.test.ts",{"duration":3.686000000000007,"failed":false}],[":src/__tests__/select.test.ts",{"duration":2.8540000000000134,"failed":false}],[":src/__tests__/floating-surface.test.ts",{"duration":4.650791999999996,"failed":false}]]}
1
+ {"version":"3.2.4","results":[[":src/machines/dialog.test.ts",{"duration":5.771292000000017,"failed":false}],[":src/machines/select.test.ts",{"duration":6.394416000000035,"failed":false}],[":src/__tests__/dialog.test.ts",{"duration":3.686000000000007,"failed":false}],[":src/__tests__/select.test.ts",{"duration":2.8540000000000134,"failed":false}],[":src/__tests__/floating-surface.test.ts",{"duration":6.529208000000011,"failed":false}],[":src/__tests__/app-nav.test.ts",{"duration":7.017959000000019,"failed":false}]]}
@@ -0,0 +1,30 @@
1
+ import { createActor } from 'xstate'
2
+ import { describe, expect, it } from 'vitest'
3
+ import {
4
+ appNavChild,
5
+ appNavMachine,
6
+ appNavStateAttr,
7
+ } from '../machines/app-nav.js'
8
+
9
+ describe('appNavMachine', () => {
10
+ it('switches section and clears stack', () => {
11
+ const actor = createActor(appNavMachine, { input: { section: 'home' } }).start()
12
+ actor.send({ type: 'PUSH', child: { id: '1', type: 'detail' } })
13
+ expect(appNavChild(actor.getSnapshot().context)?.id).toBe('1')
14
+ actor.send({ type: 'SET_SECTION', section: 'settings' })
15
+ const ctx = actor.getSnapshot().context
16
+ expect(ctx.section).toBe('settings')
17
+ expect(ctx.stack).toEqual([])
18
+ })
19
+
20
+ it('push / pop / popTo maintain stack', () => {
21
+ const actor = createActor(appNavMachine, { input: { section: 'apps' } }).start()
22
+ actor.send({ type: 'PUSH', child: { id: 'a', type: 'app' } })
23
+ actor.send({ type: 'PUSH', child: { id: 'p', type: 'permission' } })
24
+ expect(appNavStateAttr(actor.getSnapshot().context)).toBe('apps/permission')
25
+ actor.send({ type: 'POP_TO', id: 'a' })
26
+ expect(appNavChild(actor.getSnapshot().context)?.id).toBe('a')
27
+ actor.send({ type: 'POP' })
28
+ expect(appNavChild(actor.getSnapshot().context)).toBeNull()
29
+ })
30
+ })
@@ -10,5 +10,6 @@ export * from './machines/popover.js'
10
10
  export * from './machines/toast.js'
11
11
  export * from './machines/slider.js'
12
12
  export * from './machines/app-shell.js'
13
+ export * from './machines/app-nav.js'
13
14
  export * from './machines/sticky-bar.js'
14
15
  export * from './machines/content-region.js'
@@ -0,0 +1,107 @@
1
+ import { assign, setup } from 'xstate'
2
+
3
+ /**
4
+ * Section + detail stack navigation — the pattern every multi-panel product
5
+ * reinvented (wallet sections, settings drill-in, list→detail).
6
+ *
7
+ * Not product-specific: `section` is a string id; `stack` is opaque child records.
8
+ */
9
+ export type AppNavChild = {
10
+ id: string
11
+ /** Free-form type tag (e.g. "detail", "compose", "settings"). */
12
+ type: string
13
+ /** Optional payload for the view layer. */
14
+ data?: unknown
15
+ }
16
+
17
+ export interface AppNavContext {
18
+ section: string
19
+ stack: AppNavChild[]
20
+ }
21
+
22
+ export type AppNavEvent =
23
+ | { type: 'SET_SECTION'; section: string }
24
+ | { type: 'PUSH'; child: AppNavChild }
25
+ | { type: 'REPLACE'; child: AppNavChild }
26
+ | { type: 'POP' }
27
+ | { type: 'POP_TO'; id: string }
28
+ | { type: 'CLEAR' }
29
+ | { type: 'RESET'; section: string }
30
+
31
+ export function appNavDepth(ctx: AppNavContext): number {
32
+ return ctx.stack.length
33
+ }
34
+
35
+ export function appNavChild(ctx: AppNavContext): AppNavChild | null {
36
+ return ctx.stack.length ? ctx.stack[ctx.stack.length - 1]! : null
37
+ }
38
+
39
+ /** Projected state for data-aeon-state: `section` or `section/childType`. */
40
+ export function appNavStateAttr(ctx: AppNavContext): string {
41
+ const child = appNavChild(ctx)
42
+ return child ? `${ctx.section}/${child.type}` : ctx.section
43
+ }
44
+
45
+ export const appNavMachine = setup({
46
+ types: {
47
+ context: {} as AppNavContext,
48
+ events: {} as AppNavEvent,
49
+ input: {} as Partial<AppNavContext> & { section?: string },
50
+ },
51
+ actions: {
52
+ setSection: assign({
53
+ section: ({ event }) => (event.type === 'SET_SECTION' ? event.section : ''),
54
+ stack: [],
55
+ }),
56
+ push: assign({
57
+ stack: ({ context, event }) =>
58
+ event.type === 'PUSH' ? [...context.stack, event.child] : context.stack,
59
+ }),
60
+ replace: assign({
61
+ stack: ({ context, event }) => {
62
+ if (event.type !== 'REPLACE') return context.stack
63
+ if (!context.stack.length) return [event.child]
64
+ return [...context.stack.slice(0, -1), event.child]
65
+ },
66
+ }),
67
+ pop: assign({
68
+ stack: ({ context }) => context.stack.slice(0, -1),
69
+ }),
70
+ popTo: assign({
71
+ stack: ({ context, event }) => {
72
+ if (event.type !== 'POP_TO') return context.stack
73
+ const idx = context.stack.findIndex((c) => c.id === event.id)
74
+ if (idx < 0) return context.stack
75
+ return context.stack.slice(0, idx + 1)
76
+ },
77
+ }),
78
+ clear: assign({ stack: [] }),
79
+ reset: assign({
80
+ section: ({ event }) => (event.type === 'RESET' ? event.section : ''),
81
+ stack: [],
82
+ }),
83
+ },
84
+ guards: {
85
+ canPop: ({ context }) => context.stack.length > 0,
86
+ },
87
+ }).createMachine({
88
+ id: 'appNav',
89
+ context: ({ input }) => ({
90
+ section: input?.section ?? 'home',
91
+ stack: input?.stack ?? [],
92
+ }),
93
+ initial: 'ready',
94
+ states: {
95
+ ready: {
96
+ on: {
97
+ SET_SECTION: { actions: 'setSection' },
98
+ PUSH: { actions: 'push' },
99
+ REPLACE: { actions: 'replace' },
100
+ POP: { guard: 'canPop', actions: 'pop' },
101
+ POP_TO: { actions: 'popTo' },
102
+ CLEAR: { actions: 'clear' },
103
+ RESET: { actions: 'reset' },
104
+ },
105
+ },
106
+ },
107
+ })