@teacharium/widget 0.2.1 → 0.3.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,11 @@
1
+ import * as React from "react";
2
+ interface AudioPlayerProps {
3
+ audioUrl: string;
4
+ className?: string;
5
+ size?: "sm" | "md" | "lg";
6
+ onPlay?: () => void;
7
+ onPause?: () => void;
8
+ onError?: (error: string) => void;
9
+ }
10
+ export declare const AudioPlayer: React.FC<AudioPlayerProps>;
11
+ export {};
package/dist/main.d.ts CHANGED
@@ -2,4 +2,7 @@ export * from './teacharium-widget';
2
2
  export * from './teacharium-widget-api';
3
3
  export * from './teacharium-custom-fields';
4
4
  export * from './standard-layout-options';
5
+ export { AudioPlayer } from './components/AudioPlayer';
6
+ export * from './utils/voiceover';
7
+ export * from './utils/actions';
5
8
  export declare const hi = "world";
@@ -1,34 +1,66 @@
1
1
  import { BaseField } from '@measured/puck';
2
- export interface ImageMediaField extends BaseField {
2
+ export type TeachariumCustomFieldType = "imageMedia" | "videoMedia" | "audioMedia" | "voiceover" | "action" | "variable" | "numericExpression" | "stringExpression" | "placeholder" | "step" | "condition" | "textStyle" | "frame";
3
+ export type TeachariumCustomField = BaseField & {
4
+ type: TeachariumCustomFieldType;
5
+ label?: string;
6
+ placeholder?: string;
7
+ description?: string;
8
+ value?: string;
9
+ };
10
+ export type ImageMediaField = TeachariumCustomField & {
3
11
  type: "imageMedia";
4
12
  label: string;
5
- }
6
- export interface VideoMediaField extends BaseField {
13
+ };
14
+ export type VideoMediaField = TeachariumCustomField & {
7
15
  type: "videoMedia";
8
16
  label: string;
9
- }
10
- export interface AudioMediaField extends BaseField {
17
+ };
18
+ export type AudioMediaField = TeachariumCustomField & {
11
19
  type: "audioMedia";
12
20
  label: string;
13
- }
14
- export interface ActionField extends BaseField {
21
+ };
22
+ export type VoiceoverField = TeachariumCustomField & {
23
+ type: "voiceover";
24
+ label?: string;
25
+ };
26
+ export type ActionField = TeachariumCustomField & {
15
27
  type: "action";
16
28
  label: string;
17
- }
18
- export interface VariableField extends BaseField {
29
+ };
30
+ export type VariableField = TeachariumCustomField & {
19
31
  type: "variable";
20
32
  label: string;
21
- }
22
- export interface NumericExpressionField extends BaseField {
33
+ };
34
+ export type NumericExpressionField = TeachariumCustomField & {
23
35
  type: "numericExpression";
24
36
  label: string;
25
- }
26
- export interface StringExpressionField extends BaseField {
37
+ };
38
+ export type StringExpressionField = TeachariumCustomField & {
27
39
  type: "stringExpression";
28
40
  label: string;
29
- }
30
- export interface ConditionField extends BaseField {
41
+ };
42
+ export type ConditionField = TeachariumCustomField & {
31
43
  type: "condition";
32
44
  label: string;
33
- }
34
- export type TeachariumCustomFields = ImageMediaField | VideoMediaField | AudioMediaField | ActionField | VariableField | NumericExpressionField | StringExpressionField | ConditionField;
45
+ };
46
+ export type PlaceholderField = TeachariumCustomField & {
47
+ type: "placeholder";
48
+ label?: string;
49
+ description?: string;
50
+ value?: string;
51
+ };
52
+ export type StepField = TeachariumCustomField & {
53
+ type: "step";
54
+ label: string;
55
+ placeholder?: string;
56
+ description?: string;
57
+ };
58
+ export type TextStyleField = TeachariumCustomField & {
59
+ type: "textStyle";
60
+ label?: string;
61
+ };
62
+ export type FrameField = TeachariumCustomField & {
63
+ type: "frame";
64
+ label?: string;
65
+ };
66
+ export type TeachariumCustomFields = TeachariumCustomField;
@@ -1,4 +1,14 @@
1
1
  import { default as React } from 'react';
2
+ export type VoiceType = "alloy" | "ash" | "ballad" | "coral" | "echo" | "fable" | "onyx" | "nova" | "sage" | "shimmer" | "verse";
3
+ export interface VoiceoverData {
4
+ enabled: boolean;
5
+ voice: VoiceType;
6
+ speed?: number;
7
+ showPlayButton: boolean;
8
+ cached?: boolean;
9
+ content?: string;
10
+ contentHash?: string;
11
+ }
2
12
  export declare const EVENT_SOURCE_PLAYER = "player";
3
13
  export declare const EVENT_SOURCE_FOOTER = "footer";
4
14
  type EventListener<T = any> = (event: T) => void;
@@ -8,21 +18,17 @@ export interface BaseEvent {
8
18
  emittedBy?: string;
9
19
  }
10
20
  export type VariableValue = string | number | boolean;
11
- export interface UseNumberExpression {
12
- (expression: string, defaultValue: number): number;
13
- (expression: string): number | null;
14
- }
15
- export interface UseStringExpression {
16
- (expression: string, defaultValue?: string): string;
21
+ export interface UseNumberExpressions {
22
+ (expressions: string[], defaultValues?: number[]): (number)[];
17
23
  }
18
- export interface UseVariableValue {
19
- (name: string): VariableValue | undefined;
24
+ export interface UseStringExpressions {
25
+ (expressions: (string | undefined)[], defaultValues?: string[]): string[];
20
26
  }
21
27
  export interface UseVariableValues {
22
- (names: string[]): (VariableValue | undefined)[];
28
+ (names: Array<string | null | undefined>): (VariableValue | undefined)[];
23
29
  }
24
- export interface UseUpdateVariable {
25
- (newValue: VariableValue): void;
30
+ export interface UseUpdateVariables {
31
+ (values: Record<string, VariableValue>): void;
26
32
  }
27
33
  export interface UseMediaUrl {
28
34
  (src: string, expiresIn?: number): {
@@ -33,6 +39,20 @@ export interface UseMediaUrl {
33
39
  isLoadingError: boolean;
34
40
  };
35
41
  }
42
+ export interface UseVoiceoverUrl {
43
+ (contentHash?: string | null, voice?: VoiceType | null, options?: {
44
+ enabled?: boolean;
45
+ model?: string | null;
46
+ }): {
47
+ data: string | undefined;
48
+ error: Error | null;
49
+ isError: boolean;
50
+ isLoading: boolean;
51
+ isLoadingError: boolean;
52
+ refetch: () => Promise<any>;
53
+ voiceoverId: string | undefined;
54
+ };
55
+ }
36
56
  export interface ConditionValue {
37
57
  leftOperand: string;
38
58
  operator: string;
@@ -46,25 +66,67 @@ export interface UseConditionEvaluationResult {
46
66
  export interface UseConditionEvaluation {
47
67
  (condition: ConditionValue): UseConditionEvaluationResult;
48
68
  }
49
- export interface ItemOptions {
50
- correctVariableName: string;
51
- feedbackVariableName?: string;
52
- attemptedVariableName?: string;
69
+ export type ItemType = "instructional" | "practice" | "scored";
70
+ export interface CurrentAnswer {
71
+ humanReadable: string;
72
+ machineReadable: string | number;
53
73
  }
54
- export interface UseIsItem {
55
- (widgetId: string, options: ItemOptions): void;
74
+ export interface ItemStatus {
75
+ correct: boolean;
76
+ attempted: boolean;
77
+ currentAnswer?: CurrentAnswer;
78
+ maxScore: number;
79
+ currentScore: number;
56
80
  }
57
81
  export interface ItemData {
58
82
  widgetId: string;
59
- correctVariableName: string;
60
- feedbackVariableName?: string;
61
- attemptedVariableName?: string;
83
+ widgetName?: string;
84
+ stepId: string;
85
+ type: ItemType;
86
+ status: ItemStatus;
87
+ }
88
+ export interface ItemUpdateApi {
89
+ updateCorrect: (correct: boolean) => void;
90
+ updateAttempted: (attempted: boolean) => void;
91
+ updateAnswer: (answer: CurrentAnswer | undefined) => void;
92
+ updateStatus: (updates: Partial<ItemStatus>) => void;
93
+ updateScore: (currentScore: number, maxScore?: number) => void;
94
+ updateMaxScore: (maxScore: number) => void;
95
+ }
96
+ export interface UseIsItemOptions {
97
+ stepId: string;
98
+ type?: ItemType;
99
+ maxScore?: number;
100
+ widgetName?: string;
101
+ }
102
+ /**
103
+ * Hook for widgets to register as items and get the update API.
104
+ * When used via api.hooks.useIsItem (curried version), widgets can optionally pass configuration.
105
+ */
106
+ export interface UseIsItem {
107
+ (options?: {
108
+ widgetId?: string;
109
+ options?: Omit<UseIsItemOptions, "stepId">;
110
+ }): ItemUpdateApi;
111
+ }
112
+ /**
113
+ * Hook for registering sub-items with custom IDs (e.g., rows in a matrix).
114
+ * Requires passing a custom widget ID and optional type.
115
+ */
116
+ export interface UseIsItemWithId {
117
+ (customWidgetId: string, options?: {
118
+ type?: ItemType;
119
+ maxScore?: number;
120
+ widgetName?: string;
121
+ }): ItemUpdateApi;
62
122
  }
63
123
  export interface ItemsContextValue {
64
124
  items: Map<string, ItemData>;
65
- registerItem: (widgetId: string, options: ItemOptions) => void;
125
+ registerItem: (widgetId: string, stepId: string, type?: ItemType, maxScore?: number, widgetName?: string) => void;
66
126
  unregisterItem: (widgetId: string) => void;
67
127
  getItems: () => ItemData[];
128
+ getItemsByStepId: (stepId: string) => ItemData[];
129
+ getItemUpdateApi: (widgetId: string) => ItemUpdateApi;
68
130
  }
69
131
  export interface UseItemsContext {
70
132
  (): ItemsContextValue;
@@ -78,6 +140,7 @@ export interface UseLessonEventSubscription {
78
140
  export interface TeachariumWidgetApi {
79
141
  isEditing: boolean;
80
142
  playerMode: "editing" | "preview" | "playbook";
143
+ stepId: string | undefined;
81
144
  events: {
82
145
  /**
83
146
  * Emit an event with type and params
@@ -88,28 +151,50 @@ export interface TeachariumWidgetApi {
88
151
  unsubscribe(eventType: string, listener: EventListener): void;
89
152
  };
90
153
  React: any;
154
+ updateVariables: (values: Record<string, VariableValue>) => void;
91
155
  hooks: {
92
- useNumberExpression: UseNumberExpression;
93
- useStringExpression: UseStringExpression;
94
- useVariableValue: UseVariableValue;
156
+ useNumberExpressions: UseNumberExpressions;
157
+ useStringExpressions: UseStringExpressions;
95
158
  useVariableValues: UseVariableValues;
96
- useUpdateVariable: (variableName: string) => UseUpdateVariable;
97
159
  useMediaUrl: UseMediaUrl;
160
+ useVoiceoverUrl: UseVoiceoverUrl;
98
161
  useConditionEvaluation: UseConditionEvaluation;
99
162
  useIsItem: UseIsItem;
163
+ useIsItemWithId: UseIsItemWithId;
100
164
  useItemsContext: UseItemsContext;
101
165
  useLessonEventEmitter: UseLessonEventEmitter;
102
166
  useLessonEventSubscription: UseLessonEventSubscription;
103
167
  };
104
168
  /**
105
- * Create a transient variable that exists only during runtime
106
- * Automatically prefixes the variable name with the widget ID
169
+ * Create a variable that can be either transient (runtime only) or persistent (lesson-wide)
170
+ * By default, creates a transient variable automatically prefixed with the widget ID
107
171
  * Example: addVariable('selectedAnswer', 0) might create 'mcwidget1_selectedAnswer'
108
- * @param name - Base name for the variable (will be prefixed with widget ID)
172
+ *
173
+ * For lesson-wide variables, pass { scope: "lesson", skipPrefix: true }
174
+ * Example: addVariable('TotalTime', 0, "number", { scope: "lesson", skipPrefix: true })
175
+ *
176
+ * @param name - Base name for the variable (will be prefixed with widget ID unless skipPrefix is true)
109
177
  * @param initialValue - Initial value for the variable
110
178
  * @param type - Optional type specification (auto-inferred if not provided)
179
+ * @param options - Optional configuration for the variable
111
180
  * @returns The full variable name that was created
112
181
  */
113
- addVariable: (name: string, initialValue: VariableValue, type?: "string" | "number" | "boolean") => string;
182
+ addVariable: (name: string, initialValue: VariableValue, type?: "string" | "number" | "boolean", options?: {
183
+ scope?: "lesson" | "step";
184
+ skipPrefix?: boolean;
185
+ description?: string;
186
+ }) => string;
187
+ /**
188
+ * Increment a numeric variable by a specified amount
189
+ * @param name - Variable name to increment
190
+ * @param amount - Amount to increment by (default: 1)
191
+ */
192
+ incrementVariable: (name: string, amount?: number) => void;
193
+ /**
194
+ * Decrement a numeric variable by a specified amount
195
+ * @param name - Variable name to decrement
196
+ * @param amount - Amount to decrement by (default: 1)
197
+ */
198
+ decrementVariable: (name: string, amount?: number) => void;
114
199
  }
115
200
  export {};
@@ -1,3 +1,4 @@
1
+ import { default as React } from 'react';
1
2
  import { DefaultComponentProps, Fields, PuckComponent } from '@measured/puck';
2
3
  import { TeachariumWidgetApi } from './teacharium-widget-api';
3
4
  import { TeachariumCustomFields } from './teacharium-custom-fields';
@@ -5,13 +6,26 @@ interface TeachariumComponentProps {
5
6
  api: TeachariumWidgetApi;
6
7
  }
7
8
  export interface TeachariumComponentConfig<T = {}> {
9
+ usagePrompt: string;
8
10
  fields: Fields<DefaultComponentProps, TeachariumCustomFields>;
9
11
  defaultProps: DefaultComponentProps;
12
+ resolveData?: (arg: {
13
+ props: any;
14
+ changed: any;
15
+ }) => Promise<any>;
16
+ resolveFields?: (data: unknown, context: {
17
+ fields: Fields<DefaultComponentProps, TeachariumCustomFields>;
18
+ }) => Fields<DefaultComponentProps, TeachariumCustomFields>;
10
19
  render: PuckComponent<T & TeachariumComponentProps>;
11
20
  }
12
21
  export interface TeachariumWidget<T> {
13
22
  name: string;
14
23
  category: string;
24
+ description?: string;
25
+ aiPrompt?: string;
26
+ icon?: React.ComponentType<{
27
+ className?: string;
28
+ }> | string;
15
29
  config: TeachariumComponentConfig<T>;
16
30
  }
17
31
  export declare const registerWidget: (widget: TeachariumWidget<any>) => void;
@@ -1,8 +1,10 @@
1
- import re from "react";
2
- const ce = (n) => {
1
+ import * as x from "react";
2
+ import oe from "react";
3
+ import { VolumeX as Q, Loader2 as ae, Pause as se, Play as ce, Volume2 as ie } from "lucide-react";
4
+ const be = (n) => {
3
5
  window.TeachariumComponents = window.TeachariumComponents || {}, window.TeachariumComponents[n.name] = n;
4
- }, le = () => Object.values(window.TeachariumComponents || {}), ie = "player", fe = "footer";
5
- var p = { exports: {} }, _ = {};
6
+ }, Re = () => Object.values(window.TeachariumComponents || {}), Te = "player", ye = "footer";
7
+ var M = { exports: {} }, k = {};
6
8
  /**
7
9
  * @license React
8
10
  * react-jsx-runtime.production.js
@@ -12,29 +14,29 @@ var p = { exports: {} }, _ = {};
12
14
  * This source code is licensed under the MIT license found in the
13
15
  * LICENSE file in the root directory of this source tree.
14
16
  */
15
- var I;
16
- function te() {
17
- if (I) return _;
18
- I = 1;
19
- var n = Symbol.for("react.transitional.element"), i = Symbol.for("react.fragment");
20
- function s(m, u, c) {
21
- var E = null;
22
- if (c !== void 0 && (E = "" + c), u.key !== void 0 && (E = "" + u.key), "key" in u) {
23
- c = {};
24
- for (var R in u)
25
- R !== "key" && (c[R] = u[R]);
26
- } else c = u;
27
- return u = c.ref, {
17
+ var K;
18
+ function le() {
19
+ if (K) return k;
20
+ K = 1;
21
+ var n = Symbol.for("react.transitional.element"), o = Symbol.for("react.fragment");
22
+ function t(m, i, l) {
23
+ var s = null;
24
+ if (l !== void 0 && (s = "" + l), i.key !== void 0 && (s = "" + i.key), "key" in i) {
25
+ l = {};
26
+ for (var E in i)
27
+ E !== "key" && (l[E] = i[E]);
28
+ } else l = i;
29
+ return i = l.ref, {
28
30
  $$typeof: n,
29
31
  type: m,
30
- key: E,
31
- ref: u !== void 0 ? u : null,
32
- props: c
32
+ key: s,
33
+ ref: i !== void 0 ? i : null,
34
+ props: l
33
35
  };
34
36
  }
35
- return _.Fragment = i, _.jsx = s, _.jsxs = s, _;
37
+ return k.Fragment = o, k.jsx = t, k.jsxs = t, k;
36
38
  }
37
- var b = {};
39
+ var O = {};
38
40
  /**
39
41
  * @license React
40
42
  * react-jsx-runtime.development.js
@@ -44,44 +46,44 @@ var b = {};
44
46
  * This source code is licensed under the MIT license found in the
45
47
  * LICENSE file in the root directory of this source tree.
46
48
  */
47
- var L;
48
- function ne() {
49
- return L || (L = 1, process.env.NODE_ENV !== "production" && (function() {
49
+ var ee;
50
+ function ue() {
51
+ return ee || (ee = 1, process.env.NODE_ENV !== "production" && (function() {
50
52
  function n(e) {
51
53
  if (e == null) return null;
52
54
  if (typeof e == "function")
53
- return e.$$typeof === Q ? null : e.displayName || e.name || null;
55
+ return e.$$typeof === I ? null : e.displayName || e.name || null;
54
56
  if (typeof e == "string") return e;
55
57
  switch (e) {
56
- case v:
58
+ case y:
57
59
  return "Fragment";
58
- case q:
60
+ case W:
59
61
  return "Profiler";
60
- case V:
62
+ case R:
61
63
  return "StrictMode";
62
- case X:
64
+ case _:
63
65
  return "Suspense";
64
- case B:
66
+ case N:
65
67
  return "SuspenseList";
66
- case Z:
68
+ case Y:
67
69
  return "Activity";
68
70
  }
69
71
  if (typeof e == "object")
70
72
  switch (typeof e.tag == "number" && console.error(
71
73
  "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
72
74
  ), e.$$typeof) {
73
- case U:
75
+ case $:
74
76
  return "Portal";
75
- case z:
77
+ case S:
76
78
  return (e.displayName || "Context") + ".Provider";
77
- case J:
79
+ case D:
78
80
  return (e._context.displayName || "Context") + ".Consumer";
79
- case G:
81
+ case c:
80
82
  var r = e.render;
81
83
  return e = e.displayName, e || (e = r.displayName || r.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
82
- case H:
84
+ case L:
83
85
  return r = e.displayName || null, r !== null ? r : n(e.type) || "Memo";
84
- case y:
86
+ case j:
85
87
  r = e._payload, e = e._init;
86
88
  try {
87
89
  return n(e(r));
@@ -90,29 +92,29 @@ function ne() {
90
92
  }
91
93
  return null;
92
94
  }
93
- function i(e) {
95
+ function o(e) {
94
96
  return "" + e;
95
97
  }
96
- function s(e) {
98
+ function t(e) {
97
99
  try {
98
- i(e);
100
+ o(e);
99
101
  var r = !1;
100
102
  } catch {
101
103
  r = !0;
102
104
  }
103
105
  if (r) {
104
106
  r = console;
105
- var t = r.error, o = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
106
- return t.call(
107
+ var a = r.error, u = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
108
+ return a.call(
107
109
  r,
108
110
  "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
109
- o
110
- ), i(e);
111
+ u
112
+ ), o(e);
111
113
  }
112
114
  }
113
115
  function m(e) {
114
- if (e === v) return "<>";
115
- if (typeof e == "object" && e !== null && e.$$typeof === y)
116
+ if (e === y) return "<>";
117
+ if (typeof e == "object" && e !== null && e.$$typeof === j)
116
118
  return "<...>";
117
119
  try {
118
120
  var r = n(e);
@@ -121,48 +123,48 @@ function ne() {
121
123
  return "<...>";
122
124
  }
123
125
  }
124
- function u() {
125
- var e = O.A;
126
+ function i() {
127
+ var e = h.A;
126
128
  return e === null ? null : e.getOwner();
127
129
  }
128
- function c() {
130
+ function l() {
129
131
  return Error("react-stack-top-frame");
130
132
  }
131
- function E(e) {
132
- if (g.call(e, "key")) {
133
+ function s(e) {
134
+ if (G.call(e, "key")) {
133
135
  var r = Object.getOwnPropertyDescriptor(e, "key").get;
134
136
  if (r && r.isReactWarning) return !1;
135
137
  }
136
138
  return e.key !== void 0;
137
139
  }
138
- function R(e, r) {
139
- function t() {
140
- N || (N = !0, console.error(
140
+ function E(e, r) {
141
+ function a() {
142
+ H || (H = !0, console.error(
141
143
  "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
142
144
  r
143
145
  ));
144
146
  }
145
- t.isReactWarning = !0, Object.defineProperty(e, "key", {
146
- get: t,
147
+ a.isReactWarning = !0, Object.defineProperty(e, "key", {
148
+ get: a,
147
149
  configurable: !0
148
150
  });
149
151
  }
150
- function W() {
152
+ function w() {
151
153
  var e = n(this.type);
152
- return C[e] || (C[e] = !0, console.error(
154
+ return z[e] || (z[e] = !0, console.error(
153
155
  "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
154
156
  )), e = this.props.ref, e !== void 0 ? e : null;
155
157
  }
156
- function M(e, r, t, o, f, l, w, S) {
157
- return t = l.ref, e = {
158
- $$typeof: h,
158
+ function A(e, r, a, u, T, p, V, J) {
159
+ return a = p.ref, e = {
160
+ $$typeof: C,
159
161
  type: e,
160
162
  key: r,
161
- props: l,
162
- _owner: f
163
- }, (t !== void 0 ? t : null) !== null ? Object.defineProperty(e, "ref", {
163
+ props: p,
164
+ _owner: T
165
+ }, (a !== void 0 ? a : null) !== null ? Object.defineProperty(e, "ref", {
164
166
  enumerable: !1,
165
- get: W
167
+ get: w
166
168
  }) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
167
169
  configurable: !1,
168
170
  enumerable: !1,
@@ -177,112 +179,112 @@ function ne() {
177
179
  configurable: !1,
178
180
  enumerable: !1,
179
181
  writable: !0,
180
- value: w
182
+ value: V
181
183
  }), Object.defineProperty(e, "_debugTask", {
182
184
  configurable: !1,
183
185
  enumerable: !1,
184
186
  writable: !0,
185
- value: S
187
+ value: J
186
188
  }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
187
189
  }
188
- function j(e, r, t, o, f, l, w, S) {
189
- var a = r.children;
190
- if (a !== void 0)
191
- if (o)
192
- if (K(a)) {
193
- for (o = 0; o < a.length; o++)
194
- P(a[o]);
195
- Object.freeze && Object.freeze(a);
190
+ function v(e, r, a, u, T, p, V, J) {
191
+ var d = r.children;
192
+ if (d !== void 0)
193
+ if (u)
194
+ if (te(d)) {
195
+ for (u = 0; u < d.length; u++)
196
+ P(d[u]);
197
+ Object.freeze && Object.freeze(d);
196
198
  } else
197
199
  console.error(
198
200
  "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
199
201
  );
200
- else P(a);
201
- if (g.call(r, "key")) {
202
- a = n(e);
203
- var d = Object.keys(r).filter(function(ee) {
204
- return ee !== "key";
202
+ else P(d);
203
+ if (G.call(r, "key")) {
204
+ d = n(e);
205
+ var g = Object.keys(r).filter(function(ne) {
206
+ return ne !== "key";
205
207
  });
206
- o = 0 < d.length ? "{key: someKey, " + d.join(": ..., ") + ": ...}" : "{key: someKey}", F[a + o] || (d = 0 < d.length ? "{" + d.join(": ..., ") + ": ...}" : "{}", console.error(
208
+ u = 0 < g.length ? "{key: someKey, " + g.join(": ..., ") + ": ...}" : "{key: someKey}", Z[d + u] || (g = 0 < g.length ? "{" + g.join(": ..., ") + ": ...}" : "{}", console.error(
207
209
  `A props object containing a "key" prop is being spread into JSX:
208
210
  let props = %s;
209
211
  <%s {...props} />
210
212
  React keys must be passed directly to JSX without using spread:
211
213
  let props = %s;
212
214
  <%s key={someKey} {...props} />`,
213
- o,
214
- a,
215
+ u,
215
216
  d,
216
- a
217
- ), F[a + o] = !0);
217
+ g,
218
+ d
219
+ ), Z[d + u] = !0);
218
220
  }
219
- if (a = null, t !== void 0 && (s(t), a = "" + t), E(r) && (s(r.key), a = "" + r.key), "key" in r) {
220
- t = {};
221
- for (var A in r)
222
- A !== "key" && (t[A] = r[A]);
223
- } else t = r;
224
- return a && R(
225
- t,
221
+ if (d = null, a !== void 0 && (t(a), d = "" + a), s(r) && (t(r.key), d = "" + r.key), "key" in r) {
222
+ a = {};
223
+ for (var q in r)
224
+ q !== "key" && (a[q] = r[q]);
225
+ } else a = r;
226
+ return d && E(
227
+ a,
226
228
  typeof e == "function" ? e.displayName || e.name || "Unknown" : e
227
- ), M(
229
+ ), A(
228
230
  e,
231
+ d,
232
+ p,
233
+ T,
234
+ i(),
229
235
  a,
230
- l,
231
- f,
232
- u(),
233
- t,
234
- w,
235
- S
236
+ V,
237
+ J
236
238
  );
237
239
  }
238
240
  function P(e) {
239
- typeof e == "object" && e !== null && e.$$typeof === h && e._store && (e._store.validated = 1);
241
+ typeof e == "object" && e !== null && e.$$typeof === C && e._store && (e._store.validated = 1);
240
242
  }
241
- var T = re, h = Symbol.for("react.transitional.element"), U = Symbol.for("react.portal"), v = Symbol.for("react.fragment"), V = Symbol.for("react.strict_mode"), q = Symbol.for("react.profiler"), J = Symbol.for("react.consumer"), z = Symbol.for("react.context"), G = Symbol.for("react.forward_ref"), X = Symbol.for("react.suspense"), B = Symbol.for("react.suspense_list"), H = Symbol.for("react.memo"), y = Symbol.for("react.lazy"), Z = Symbol.for("react.activity"), Q = Symbol.for("react.client.reference"), O = T.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, g = Object.prototype.hasOwnProperty, K = Array.isArray, k = console.createTask ? console.createTask : function() {
243
+ var b = oe, C = Symbol.for("react.transitional.element"), $ = Symbol.for("react.portal"), y = Symbol.for("react.fragment"), R = Symbol.for("react.strict_mode"), W = Symbol.for("react.profiler"), D = Symbol.for("react.consumer"), S = Symbol.for("react.context"), c = Symbol.for("react.forward_ref"), _ = Symbol.for("react.suspense"), N = Symbol.for("react.suspense_list"), L = Symbol.for("react.memo"), j = Symbol.for("react.lazy"), Y = Symbol.for("react.activity"), I = Symbol.for("react.client.reference"), h = b.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, G = Object.prototype.hasOwnProperty, te = Array.isArray, U = console.createTask ? console.createTask : function() {
242
244
  return null;
243
245
  };
244
- T = {
246
+ b = {
245
247
  react_stack_bottom_frame: function(e) {
246
248
  return e();
247
249
  }
248
250
  };
249
- var N, C = {}, Y = T.react_stack_bottom_frame.bind(
250
- T,
251
- c
252
- )(), $ = k(m(c)), F = {};
253
- b.Fragment = v, b.jsx = function(e, r, t, o, f) {
254
- var l = 1e4 > O.recentlyCreatedOwnerStacks++;
255
- return j(
251
+ var H, z = {}, X = b.react_stack_bottom_frame.bind(
252
+ b,
253
+ l
254
+ )(), B = U(m(l)), Z = {};
255
+ O.Fragment = y, O.jsx = function(e, r, a, u, T) {
256
+ var p = 1e4 > h.recentlyCreatedOwnerStacks++;
257
+ return v(
256
258
  e,
257
259
  r,
258
- t,
260
+ a,
259
261
  !1,
260
- o,
261
- f,
262
- l ? Error("react-stack-top-frame") : Y,
263
- l ? k(m(e)) : $
262
+ u,
263
+ T,
264
+ p ? Error("react-stack-top-frame") : X,
265
+ p ? U(m(e)) : B
264
266
  );
265
- }, b.jsxs = function(e, r, t, o, f) {
266
- var l = 1e4 > O.recentlyCreatedOwnerStacks++;
267
- return j(
267
+ }, O.jsxs = function(e, r, a, u, T) {
268
+ var p = 1e4 > h.recentlyCreatedOwnerStacks++;
269
+ return v(
268
270
  e,
269
271
  r,
270
- t,
272
+ a,
271
273
  !0,
272
- o,
273
- f,
274
- l ? Error("react-stack-top-frame") : Y,
275
- l ? k(m(e)) : $
274
+ u,
275
+ T,
276
+ p ? Error("react-stack-top-frame") : X,
277
+ p ? U(m(e)) : B
276
278
  );
277
279
  };
278
- })()), b;
280
+ })()), O;
279
281
  }
280
- var D;
281
- function oe() {
282
- return D || (D = 1, process.env.NODE_ENV === "production" ? p.exports = te() : p.exports = ne()), p.exports;
282
+ var re;
283
+ function de() {
284
+ return re || (re = 1, process.env.NODE_ENV === "production" ? M.exports = le() : M.exports = ue()), M.exports;
283
285
  }
284
- var x = oe();
285
- const ae = {
286
+ var f = de();
287
+ const fe = {
286
288
  margin: {
287
289
  type: "number",
288
290
  label: "Margin",
@@ -298,26 +300,157 @@ const ae = {
298
300
  // { label: "Partial", value: "partial" },
299
301
  // ],
300
302
  // },
301
- }, se = ({ margin: n, children: i }) => {
302
- const s = {};
303
- return n && (s.padding = `${n}px`), /* @__PURE__ */ x.jsx("div", { style: s, children: i });
304
- }, de = (n) => {
305
- const i = n.render;
303
+ }, me = ({ margin: n, children: o }) => {
304
+ const t = {};
305
+ return n && (t.padding = `${n}px`), /* @__PURE__ */ f.jsx("div", { style: t, children: o });
306
+ }, _e = (n) => {
307
+ const o = n.render;
306
308
  return {
307
309
  ...n,
308
- render: (s) => /* @__PURE__ */ x.jsx(se, { ...s, children: /* @__PURE__ */ x.jsx(i, { ...s }) }),
310
+ render: (t) => /* @__PURE__ */ f.jsx(me, { ...t, children: /* @__PURE__ */ f.jsx(o, { ...t }) }),
309
311
  fields: {
310
312
  ...n.fields,
311
- ...ae
313
+ ...fe
312
314
  }
313
315
  };
314
- }, me = "world";
316
+ }, F = (...n) => n.filter(Boolean).join(" "), he = ({
317
+ audioUrl: n,
318
+ className: o,
319
+ size: t = "sm",
320
+ onPlay: m,
321
+ onPause: i,
322
+ onError: l
323
+ }) => {
324
+ const s = x.useRef(null), [E, w] = x.useState(!1), [A, v] = x.useState(!1), [P, b] = x.useState(null), [C, $] = x.useState(!1), y = {
325
+ sm: "h-6 w-6",
326
+ md: "h-8 w-8",
327
+ lg: "h-10 w-10"
328
+ }, R = {
329
+ sm: 12,
330
+ md: 16,
331
+ lg: 20
332
+ }, W = async () => {
333
+ if (s.current)
334
+ try {
335
+ v(!0), b(null), E ? s.current.pause() : await s.current.play();
336
+ } catch (c) {
337
+ const _ = c instanceof Error ? c.message : "Failed to play audio";
338
+ b(_), l?.(_);
339
+ } finally {
340
+ v(!1);
341
+ }
342
+ }, D = () => {
343
+ s.current && (s.current.muted = !s.current.muted, $(s.current.muted));
344
+ };
345
+ x.useEffect(() => {
346
+ const c = s.current;
347
+ if (!c) return;
348
+ const _ = () => {
349
+ w(!0), m?.();
350
+ }, N = () => {
351
+ w(!1), i?.();
352
+ }, L = () => {
353
+ w(!1), i?.();
354
+ }, j = () => {
355
+ v(!0);
356
+ }, Y = () => {
357
+ v(!1);
358
+ }, I = () => {
359
+ const h = "Failed to load audio";
360
+ b(h), v(!1), l?.(h);
361
+ };
362
+ return c.addEventListener("play", _), c.addEventListener("pause", N), c.addEventListener("ended", L), c.addEventListener("loadstart", j), c.addEventListener("canplay", Y), c.addEventListener("error", I), () => {
363
+ c.removeEventListener("play", _), c.removeEventListener("pause", N), c.removeEventListener("ended", L), c.removeEventListener("loadstart", j), c.removeEventListener("canplay", Y), c.removeEventListener("error", I);
364
+ };
365
+ }, [m, i, l]);
366
+ const S = "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-background bg-transparent hover:bg-gray-100";
367
+ return P ? /* @__PURE__ */ f.jsx(
368
+ "button",
369
+ {
370
+ type: "button",
371
+ className: F(
372
+ S,
373
+ y[t],
374
+ "text-red-500",
375
+ o
376
+ ),
377
+ disabled: !0,
378
+ children: /* @__PURE__ */ f.jsx(Q, { size: R[t] })
379
+ }
380
+ ) : /* @__PURE__ */ f.jsxs("div", { className: F("flex items-center gap-1", o), children: [
381
+ /* @__PURE__ */ f.jsx("audio", { ref: s, src: n, preload: "metadata" }),
382
+ /* @__PURE__ */ f.jsx(
383
+ "button",
384
+ {
385
+ type: "button",
386
+ className: F(S, y[t]),
387
+ onClick: W,
388
+ disabled: A,
389
+ children: A ? /* @__PURE__ */ f.jsx(ae, { size: R[t], className: "animate-spin" }) : E ? /* @__PURE__ */ f.jsx(se, { size: R[t] }) : /* @__PURE__ */ f.jsx(ce, { size: R[t] })
390
+ }
391
+ ),
392
+ t !== "sm" && /* @__PURE__ */ f.jsx(
393
+ "button",
394
+ {
395
+ type: "button",
396
+ className: F(S, y[t]),
397
+ onClick: D,
398
+ children: C ? /* @__PURE__ */ f.jsx(Q, { size: R[t] }) : /* @__PURE__ */ f.jsx(ie, { size: R[t] })
399
+ }
400
+ )
401
+ ] });
402
+ }, Ee = "SHA-256";
403
+ async function ge(n) {
404
+ const o = globalThis.crypto?.subtle;
405
+ if (!o)
406
+ throw new Error("Web Crypto is not available to compute voiceover hashes");
407
+ const m = new TextEncoder().encode(n), i = await o.digest(Ee, m), l = new Uint8Array(i);
408
+ let s = "";
409
+ for (let E = 0; E < l.length; E += 1)
410
+ s += l[E].toString(16).padStart(2, "0");
411
+ return s;
412
+ }
413
+ function xe(n, o) {
414
+ !o || o.length === 0 || o.forEach((t) => {
415
+ if (!t.actionType) {
416
+ console.warn("Skipping action with no actionType:", t);
417
+ return;
418
+ }
419
+ try {
420
+ n.events.emit({
421
+ type: t.actionType,
422
+ params: t.parameters || {}
423
+ });
424
+ } catch (m) {
425
+ console.error(`Error executing action ${t.actionType}:`, m);
426
+ }
427
+ });
428
+ }
429
+ function we(n, o) {
430
+ if (!o || !o.actionType) {
431
+ console.warn("Cannot execute action with no actionType:", o);
432
+ return;
433
+ }
434
+ try {
435
+ n.events.emit({
436
+ type: o.actionType,
437
+ params: o.parameters || {}
438
+ });
439
+ } catch (t) {
440
+ console.error(`Error executing action ${o.actionType}:`, t);
441
+ }
442
+ }
443
+ const Se = "world";
315
444
  export {
316
- fe as EVENT_SOURCE_FOOTER,
317
- ie as EVENT_SOURCE_PLAYER,
318
- se as LayoutContainer,
319
- de as addStandardLayoutOptions,
320
- le as getWidgets,
321
- me as hi,
322
- ce as registerWidget
445
+ he as AudioPlayer,
446
+ ye as EVENT_SOURCE_FOOTER,
447
+ Te as EVENT_SOURCE_PLAYER,
448
+ me as LayoutContainer,
449
+ _e as addStandardLayoutOptions,
450
+ we as executeAction,
451
+ xe as executeActions,
452
+ ge as getVoiceoverHash,
453
+ Re as getWidgets,
454
+ Se as hi,
455
+ be as registerWidget
323
456
  };
@@ -0,0 +1,39 @@
1
+ import { TeachariumWidgetApi } from '../teacharium-widget-api';
2
+ /**
3
+ * Action configuration type
4
+ */
5
+ export interface ActionConfiguration {
6
+ actionType: string;
7
+ parameters: Record<string, unknown>;
8
+ }
9
+ /**
10
+ * Execute multiple actions in sequence
11
+ *
12
+ * @param api - The TeachariumWidgetApi instance
13
+ * @param actions - Array of action configurations to execute
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * executeActions(api, [
18
+ * { actionType: "setVariable", parameters: { variableName: "score", value: 10 } },
19
+ * { actionType: "showFeedback", parameters: { message: "Great!", variant: "correct" } },
20
+ * { actionType: "nextStep", parameters: {} }
21
+ * ]);
22
+ * ```
23
+ */
24
+ export declare function executeActions(api: TeachariumWidgetApi, actions: ActionConfiguration[]): void;
25
+ /**
26
+ * Execute a single action
27
+ *
28
+ * @param api - The TeachariumWidgetApi instance
29
+ * @param action - Action configuration to execute
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * executeAction(api, {
34
+ * actionType: "nextStep",
35
+ * parameters: {}
36
+ * });
37
+ * ```
38
+ */
39
+ export declare function executeAction(api: TeachariumWidgetApi, action: ActionConfiguration): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Compute a stable hash for voiceover content so the same script
3
+ * reuses cached audio. Relies solely on browser-compatible Web Crypto APIs.
4
+ */
5
+ export declare function getVoiceoverHash(content: string): Promise<string>;
package/package.json CHANGED
@@ -1,21 +1,30 @@
1
1
  {
2
2
  "name": "@teacharium/widget",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
7
7
  ],
8
8
  "module": "dist/teacharium-widget.es.js",
9
9
  "types": "dist/main.d.ts",
10
+ "scripts": {
11
+ "dev": "tsc -b && vite build --watch",
12
+ "build:watch": "tsc -b && vite build --watch",
13
+ "build": "tsc -b && vite build",
14
+ "lint": "eslint .",
15
+ "type-check": "tsc --noEmit"
16
+ },
10
17
  "dependencies": {
11
- "@measured/puck": "^0.20.2",
12
- "react": "^19.1.1",
13
- "react-dom": "^19.1.1"
18
+ "@measured/puck": "catalog:",
19
+ "@next/eslint-plugin-next": "^15.5.4",
20
+ "lucide-react": "^0.511.0",
21
+ "react": "catalog:",
22
+ "react-dom": "catalog:"
14
23
  },
15
24
  "devDependencies": {
16
25
  "@eslint/js": "^9.35.0",
17
- "@types/react": "^19.1.12",
18
- "@types/react-dom": "^19.1.9",
26
+ "@types/react": "catalog:",
27
+ "@types/react-dom": "catalog:",
19
28
  "@vitejs/plugin-react": "^5.0.2",
20
29
  "eslint": "^9.35.0",
21
30
  "eslint-plugin-react-hooks": "^5.2.0",
@@ -26,10 +35,5 @@
26
35
  "vite": "^7.1.5",
27
36
  "vite-plugin-dts": "^4.5.4"
28
37
  },
29
- "scripts": {
30
- "dev": "tsc -b && vite build --watch",
31
- "build:watch": "tsc -b && vite build --watch",
32
- "build": "tsc -b && vite build",
33
- "lint": "eslint ."
34
- }
35
- }
38
+ "packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184"
39
+ }