@osiloke/chalo 0.1.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.
@@ -0,0 +1,3 @@
1
+ export declare function SmartDrawer({ className }: {
2
+ className?: string;
3
+ }): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,7 @@
1
+ interface TargetHighlightProps {
2
+ selector: string;
3
+ label?: string;
4
+ pulse?: boolean;
5
+ }
6
+ export declare function TargetHighlight({ selector, label, pulse }: TargetHighlightProps): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,11 @@
1
+ import { Action, ActionResult, ExecutionContext, ActionHandler, ActionType } from '../types';
2
+ export declare class ActionEngine {
3
+ private handlers;
4
+ private abortController;
5
+ registerHandler(type: ActionType, handler: ActionHandler): void;
6
+ getHandler(type: ActionType): ActionHandler | undefined;
7
+ executeAction(action: Action, context: ExecutionContext): Promise<ActionResult>;
8
+ executeSequence(actions: Action[], context: ExecutionContext, onProgress?: (results: Record<string, ActionResult>) => void): Promise<Record<string, ActionResult>>;
9
+ cancel(): void;
10
+ }
11
+ export declare const actionEngine: ActionEngine;
@@ -0,0 +1 @@
1
+ export { ActionEngine, actionEngine } from './action-engine';
@@ -0,0 +1,70 @@
1
+ import { MissionId, StepId, SuccessCondition } from '../types';
2
+ import { UseFormReturn, RegisterOptions, FieldValues, Path } from 'react-hook-form';
3
+ export interface UseChaloOptions<TFieldValues extends FieldValues = FieldValues> {
4
+ form?: UseFormReturn<TFieldValues>;
5
+ onMissionComplete?: (missionId: MissionId) => void;
6
+ onStepChange?: (stepId: StepId) => void;
7
+ /** Enable debug logging to console. Default: false. */
8
+ debug?: boolean;
9
+ }
10
+ export declare function useChalo<TFieldValues extends FieldValues = FieldValues>(options?: UseChaloOptions<TFieldValues>): {
11
+ activeMissionId: string | null;
12
+ currentStepId: string | null;
13
+ isCompleted: boolean;
14
+ isPaused: boolean;
15
+ missions: Record<string, import("..").Mission>;
16
+ fieldValues: Record<string, unknown>;
17
+ fieldStates: Record<string, "idle" | "focused" | "valid" | "invalid">;
18
+ interactionHistory: import("..").ChatInteraction[];
19
+ startMission: (missionId: MissionId) => void;
20
+ pauseMission: () => void;
21
+ resumeMission: () => void;
22
+ completeMission: () => void;
23
+ goToStep: (stepId: StepId) => void;
24
+ reset: () => void;
25
+ resetMission: () => void;
26
+ dismissAllTours: () => void;
27
+ markMissionCompleted: (missionId: MissionId) => void;
28
+ completedMissions: string[];
29
+ executionContext: import("..").ExecutionContext;
30
+ registerActionHandler: (type: import("..").ActionType, handler: import("..").ActionHandler) => void;
31
+ executeAction: (action: import("..").Action) => Promise<import("..").ActionResult>;
32
+ executeActionSequence: (actions: import("..").Action[], stepId?: string) => Promise<Record<string, import("..").ActionResult>>;
33
+ cancelExecution: () => void;
34
+ addInteraction: (stepId: string, actionText: string) => void;
35
+ registerMission: (mission: import("..").Mission) => void;
36
+ activeMission: import("..").Mission | null;
37
+ currentStep: import("..").Step | null;
38
+ missionProgress: number;
39
+ fieldErrors: {};
40
+ nextStep: () => void;
41
+ prevStep: () => void;
42
+ registerField: (name: Path<TFieldValues>, rhfOptions?: RegisterOptions<TFieldValues>) => {
43
+ id: string;
44
+ name: Path<TFieldValues>;
45
+ 'data-chalo-field': string;
46
+ onChange: (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => void;
47
+ onFocus: () => void;
48
+ onBlur: () => void;
49
+ } | {
50
+ id: string;
51
+ 'data-chalo-field': string;
52
+ onChange: (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => Promise<void>;
53
+ onBlur: (e: React.FocusEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => Promise<void>;
54
+ ref: import("react-hook-form").RefCallBack;
55
+ name: Path<TFieldValues>;
56
+ min?: string | number;
57
+ max?: string | number;
58
+ maxLength?: number;
59
+ minLength?: number;
60
+ pattern?: string;
61
+ required?: boolean;
62
+ disabled?: boolean;
63
+ onFocus?: undefined;
64
+ };
65
+ registerElement: (name: string) => (el: HTMLElement | null) => void;
66
+ fillField: (name: string, value: unknown) => void;
67
+ recordTourEntry: (missionId: MissionId, stepId: StepId, completed: boolean) => void;
68
+ tourHistory: Record<string, import("..").TourEntry>;
69
+ evaluateCondition: (condition?: SuccessCondition) => boolean;
70
+ };
@@ -0,0 +1,5 @@
1
+ export * from './types';
2
+ export * from './store';
3
+ export * from './hooks/use-chalo';
4
+ export * from './components/SmartDrawer';
5
+ export * from './components/TargetHighlight';
@@ -0,0 +1,5 @@
1
+ export * from './types';
2
+ export * from './store';
3
+ export * from './hooks/use-chalo';
4
+ export * from './components/SmartDrawer';
5
+ export * from './components/TargetHighlight';
@@ -0,0 +1,14 @@
1
+ import { ChaloStore } from './types';
2
+ export declare const useChaloStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ChaloStore>, "setState" | "persist"> & {
3
+ setState(partial: ChaloStore | Partial<ChaloStore> | ((state: ChaloStore) => ChaloStore | Partial<ChaloStore>), replace?: false | undefined): unknown;
4
+ setState(state: ChaloStore | ((state: ChaloStore) => ChaloStore), replace: true): unknown;
5
+ persist: {
6
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<ChaloStore, unknown, unknown>>) => void;
7
+ clearStorage: () => void;
8
+ rehydrate: () => Promise<void> | void;
9
+ hasHydrated: () => boolean;
10
+ onHydrate: (fn: (state: ChaloStore) => void) => () => void;
11
+ onFinishHydration: (fn: (state: ChaloStore) => void) => () => void;
12
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<ChaloStore, unknown, unknown>>;
13
+ };
14
+ }>;
@@ -0,0 +1,174 @@
1
+ import { ReactNode } from 'react';
2
+ export type MissionId = string;
3
+ export type StepId = string;
4
+ export type ActionType = 'click' | 'scroll' | 'fill_field' | 'api_call' | 'wait' | 'conditional' | 'navigate' | 'custom';
5
+ export type ActionStatus = 'pending' | 'running' | 'success' | 'failed' | 'cancelled' | 'skipped';
6
+ export interface RetryConfig {
7
+ maxAttempts: number;
8
+ backoff: 'fixed' | 'exponential';
9
+ delayMs: number;
10
+ }
11
+ export interface RollbackConfig {
12
+ enabled: boolean;
13
+ }
14
+ export interface ClickActionConfig {
15
+ /** CSS selector to find the element */
16
+ selector?: string;
17
+ /** Named element registered via `registerElement` (preferred over `selector`) */
18
+ field?: string;
19
+ }
20
+ export interface ScrollActionConfig {
21
+ selector?: string;
22
+ behavior?: 'smooth' | 'instant';
23
+ }
24
+ export interface FillFieldActionConfig {
25
+ field: string;
26
+ value: unknown;
27
+ }
28
+ export interface ApiCallActionConfig {
29
+ url: string;
30
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
31
+ headers?: Record<string, string>;
32
+ body?: unknown;
33
+ }
34
+ export interface WaitActionConfig {
35
+ durationMs: number;
36
+ }
37
+ export interface ConditionalActionConfig {
38
+ condition: SuccessCondition;
39
+ thenActions?: string[];
40
+ elseActions?: string[];
41
+ }
42
+ export interface NavigateActionConfig {
43
+ path: string;
44
+ }
45
+ export interface CustomActionConfig {
46
+ handlerId: string;
47
+ params?: Record<string, unknown>;
48
+ }
49
+ export type ActionConfig = ClickActionConfig | ScrollActionConfig | FillFieldActionConfig | ApiCallActionConfig | WaitActionConfig | ConditionalActionConfig | NavigateActionConfig | CustomActionConfig;
50
+ export interface Action {
51
+ id: string;
52
+ type: ActionType;
53
+ config: ActionConfig;
54
+ label?: string;
55
+ retry?: RetryConfig;
56
+ rollback?: RollbackConfig;
57
+ dependsOn?: string[];
58
+ condition?: SuccessCondition;
59
+ }
60
+ export interface ActionResult {
61
+ id: string;
62
+ status: ActionStatus;
63
+ data?: unknown;
64
+ error?: string;
65
+ attempts: number;
66
+ startedAt?: number;
67
+ completedAt?: number;
68
+ }
69
+ export interface ExecutionContext {
70
+ results: Record<string, ActionResult>;
71
+ variables: Record<string, unknown>;
72
+ isRunning: boolean;
73
+ currentActionId: string | null;
74
+ }
75
+ export type ActionHandler = (config: ActionConfig, context: ExecutionContext) => Promise<unknown>;
76
+ export interface Mission {
77
+ id: MissionId;
78
+ title: string;
79
+ description?: string;
80
+ steps: Step[];
81
+ metadata?: Record<string, unknown>;
82
+ onComplete?: () => void;
83
+ allowCompletion?: boolean;
84
+ actions?: Action[];
85
+ }
86
+ export type BubbleType = 'message' | 'input' | 'select' | 'action-group' | 'custom';
87
+ export interface Bubble {
88
+ id: string;
89
+ type: BubbleType;
90
+ content?: string | ReactNode;
91
+ targetField?: string;
92
+ options?: Array<{
93
+ label: string;
94
+ value: unknown;
95
+ }>;
96
+ actions?: StepAction[];
97
+ }
98
+ export interface Step {
99
+ id: StepId;
100
+ title: string;
101
+ content: string | ReactNode;
102
+ bubbles?: Bubble[];
103
+ targetField?: string;
104
+ targetElement?: string;
105
+ successCondition?: SuccessCondition;
106
+ waitFor?: SuccessCondition;
107
+ condition?: SuccessCondition;
108
+ navigationRules?: {
109
+ canGoBack?: boolean;
110
+ canSkip?: boolean;
111
+ };
112
+ actions?: StepAction[];
113
+ actionSequence?: Action[];
114
+ }
115
+ export interface StepAction {
116
+ label: string;
117
+ type: 'next' | 'prev' | 'complete' | 'custom' | 'fill_field' | 'input_manual' | 'trigger_action' | 'click';
118
+ data?: unknown;
119
+ onClick?: () => void;
120
+ }
121
+ export interface SuccessCondition {
122
+ type: 'field_value' | 'field_touched' | 'custom';
123
+ field?: string;
124
+ value?: unknown;
125
+ predicate?: (value: unknown, formState: unknown) => boolean;
126
+ }
127
+ export interface ChatInteraction {
128
+ stepId: string;
129
+ actionText: string;
130
+ timestamp: number;
131
+ }
132
+ export interface TourEntry {
133
+ missionId: MissionId;
134
+ lastStepId: StepId;
135
+ completed: boolean;
136
+ lastAccessed: number;
137
+ }
138
+ export interface ChaloState {
139
+ activeMissionId: MissionId | null;
140
+ currentStepId: StepId | null;
141
+ missionProgress: number;
142
+ fieldValues: Record<string, unknown>;
143
+ fieldStates: Record<string, 'idle' | 'focused' | 'valid' | 'invalid'>;
144
+ missions: Record<string, Mission>;
145
+ interactionHistory: ChatInteraction[];
146
+ tourHistory: Record<string, TourEntry>;
147
+ completedMissions: MissionId[];
148
+ executionContext: ExecutionContext;
149
+ isPaused: boolean;
150
+ isCompleted: boolean;
151
+ error: string | null;
152
+ }
153
+ export interface ChaloStore extends ChaloState {
154
+ registerMission: (mission: Mission) => void;
155
+ startMission: (missionId: MissionId) => void;
156
+ pauseMission: () => void;
157
+ resumeMission: () => void;
158
+ completeMission: () => void;
159
+ goToStep: (stepId: StepId) => void;
160
+ nextStep: () => void;
161
+ prevStep: () => void;
162
+ updateField: (name: string, value: unknown, status?: ChaloState['fieldStates'][string]) => void;
163
+ addInteraction: (stepId: string, actionText: string) => void;
164
+ recordTourEntry: (missionId: MissionId, stepId: StepId, completed: boolean) => void;
165
+ markMissionCompleted: (missionId: MissionId) => void;
166
+ registerActionHandler: (type: ActionType, handler: ActionHandler) => void;
167
+ executeAction: (action: Action) => Promise<ActionResult>;
168
+ executeActionSequence: (actions: Action[], stepId?: string) => Promise<Record<string, ActionResult>>;
169
+ cancelExecution: () => void;
170
+ dismissAllTours: () => void;
171
+ resetMission: () => void;
172
+ reset: () => void;
173
+ setError: (error: string | null) => void;
174
+ }
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@osiloke/chalo",
3
+ "version": "0.1.0",
4
+ "description": "A modern React component library for intelligent UI interactions and guided experiences",
5
+ "type": "module",
6
+ "private": false,
7
+ "main": "./dist/chalo.cjs",
8
+ "module": "./dist/chalo.mjs",
9
+ "types": "./dist/types/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/types/index.d.mts",
14
+ "default": "./dist/chalo.mjs"
15
+ },
16
+ "require": {
17
+ "types": "./dist/types/index.d.ts",
18
+ "default": "./dist/chalo.cjs"
19
+ }
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "sideEffects": false,
26
+ "keywords": [
27
+ "react",
28
+ "component-library",
29
+ "ui",
30
+ "zustand",
31
+ "framer-motion",
32
+ "guided-experience"
33
+ ],
34
+ "author": "Osiloke Harold Emoekpere",
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/osiloke/chalo.git",
39
+ "directory": "packages/chalo"
40
+ },
41
+ "peerDependencies": {
42
+ "framer-motion": ">=12.0.0",
43
+ "react": ">=18.0.0",
44
+ "react-dom": ">=18.0.0",
45
+ "zustand": ">=5.0.0"
46
+ },
47
+ "scripts": {
48
+ "build": "npm run build:types && vite build && npm run copy:types",
49
+ "build:types": "tsc --project tsconfig.json",
50
+ "copy:types": "cp dist/types/index.d.ts dist/types/index.d.mts",
51
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
52
+ "lint:fix": "eslint . --ext ts,tsx --fix",
53
+ "format": "prettier --check .",
54
+ "format:fix": "prettier --write .",
55
+ "test": "vitest",
56
+ "test:ui": "vitest --ui",
57
+ "coverage": "vitest run --coverage",
58
+ "prepublishOnly": "vitest run --passWithNoTests && npm run build"
59
+ },
60
+ "devDependencies": {
61
+ "@testing-library/react": "^15.0.0",
62
+ "@types/react": "^19.2.14",
63
+ "@types/react-dom": "^19.2.3",
64
+ "@typescript-eslint/eslint-plugin": "^6.14.0",
65
+ "@typescript-eslint/parser": "^6.14.0",
66
+ "@vitejs/plugin-react": "^6.0.1",
67
+ "@vitest/coverage-v8": "^1.6.0",
68
+ "@vitest/ui": "^1.6.0",
69
+ "eslint": "^8.55.0",
70
+ "eslint-plugin-react-hooks": "^4.6.0",
71
+ "eslint-plugin-react-refresh": "^0.4.5",
72
+ "framer-motion": "^12.38.0",
73
+ "jsdom": "^29.0.1",
74
+ "prettier": "^3.2.0",
75
+ "react": "^19.2.4",
76
+ "react-dom": "^19.2.4",
77
+ "typescript": "^6.0.2",
78
+ "vite": "^8.0.3",
79
+ "vitest": "^1.6.0",
80
+ "zustand": "^5.0.12"
81
+ }
82
+ }