@burdenoff/microfe-buildmyiq 2026.825.1 → 2026.828.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/buildmyiq/components/TutorPanel.d.ts +13 -1
  2. package/dist/buildmyiq/components/TutorPanel.d.ts.map +1 -1
  3. package/dist/buildmyiq/components/TutorPanel.js +86 -71
  4. package/dist/buildmyiq/components/TutorPanel.js.map +1 -1
  5. package/dist/buildmyiq/hooks/useBuildmyiqApi.d.ts +34 -1
  6. package/dist/buildmyiq/hooks/useBuildmyiqApi.d.ts.map +1 -1
  7. package/dist/buildmyiq/hooks/useBuildmyiqApi.js +160 -123
  8. package/dist/buildmyiq/hooks/useBuildmyiqApi.js.map +1 -1
  9. package/dist/buildmyiq/pages/catalog/CourseDetailPage.d.ts.map +1 -1
  10. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js +251 -209
  11. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js.map +1 -1
  12. package/dist/buildmyiq/pages/learning/LessonPlayerPage.d.ts.map +1 -1
  13. package/dist/buildmyiq/pages/learning/LessonPlayerPage.js +135 -126
  14. package/dist/buildmyiq/pages/learning/LessonPlayerPage.js.map +1 -1
  15. package/dist/buildmyiq/pages/learning/MyLearningPage.d.ts.map +1 -1
  16. package/dist/buildmyiq/pages/learning/MyLearningPage.js +271 -137
  17. package/dist/buildmyiq/pages/learning/MyLearningPage.js.map +1 -1
  18. package/dist/buildmyiq/pages/submissions/SubmissionsPage.d.ts.map +1 -1
  19. package/dist/buildmyiq/pages/submissions/SubmissionsPage.js +293 -254
  20. package/dist/buildmyiq/pages/submissions/SubmissionsPage.js.map +1 -1
  21. package/package.json +1 -1
  22. package/dist/generated/global-operations.d.ts +0 -2
  23. package/dist/generated/global-operations.d.ts.map +0 -1
  24. package/dist/generated/wspace-operations.d.ts +0 -2
  25. package/dist/generated/wspace-operations.d.ts.map +0 -1
@@ -7,11 +7,23 @@
7
7
  * (see `TutorService.retrieveGrounding` in wspace-buildmyiq-svc) — never an
8
8
  * ungrounded chat completion. Continues the learner's own session for this
9
9
  * lesson (multi-turn) via `useBuiMyTutorSessions` / `useBuiTutorAsk`.
10
+ *
11
+ * Each assistant turn carries the `groundingLessonIds` the answer was actually
12
+ * derived from; they are rendered as source chips so the learner can see which
13
+ * lesson content backs the answer instead of taking it on trust. Ids the caller
14
+ * cannot name are shown as-is rather than guessed at or hidden.
10
15
  */
11
16
  interface TutorPanelProps {
12
17
  lessonId: string;
13
18
  lessonTitle: string;
19
+ /**
20
+ * Titles for the lessons the tutor may ground an answer in, keyed by lesson
21
+ * id — supplied by the host page, which has already loaded the course. Only
22
+ * used to LABEL the ids the server returned; a missing entry falls back to
23
+ * the raw id, never to an invented title.
24
+ */
25
+ lessonTitlesById?: ReadonlyMap<string, string>;
14
26
  }
15
- export declare function TutorPanel({ lessonId, lessonTitle }: TutorPanelProps): import("react").JSX.Element;
27
+ export declare function TutorPanel({ lessonId, lessonTitle, lessonTitlesById }: TutorPanelProps): import("react").JSX.Element;
16
28
  export {};
17
29
  //# sourceMappingURL=TutorPanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TutorPanel.d.ts","sourceRoot":"","sources":["../../../src/buildmyiq/components/TutorPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAmBD,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,eAAe,+BA0IpE"}
1
+ {"version":3,"file":"TutorPanel.d.ts","sourceRoot":"","sources":["../../../src/buildmyiq/components/TutorPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAWH,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChD;AAmBD,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,EAAE,eAAe,+BAkKtF"}
@@ -1,142 +1,157 @@
1
1
  import { useBuildmyiq as e } from "../context/BuildmyiqContext.js";
2
2
  import "../context/index.js";
3
- import { LoadingView as t } from "./StateViews.js";
4
- import { useBuiMyTutorSessions as n, useBuiTutorAsk as r } from "../hooks/useBuildmyiqApi.js";
3
+ import { useTr as t } from "../shared/hooks/useTr.js";
4
+ import { LoadingView as n } from "./StateViews.js";
5
+ import { useBuiMyTutorSessions as r, useBuiTutorAsk as i } from "../hooks/useBuildmyiqApi.js";
5
6
  import "../hooks/index.js";
6
- import { useEffect as i, useRef as a, useState as o } from "react";
7
- import { Bot as s, Send as c, Sparkles as l } from "lucide-react";
8
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
9
- import { Button as f, GlassCard as p, Textarea as m } from "@burdenoff/fe-libs/ui";
7
+ import { useEffect as a, useRef as o, useState as s } from "react";
8
+ import { BookOpen as c, Bot as l, Send as u, Sparkles as d } from "lucide-react";
9
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
10
+ import { Button as m, GlassCard as h, Textarea as g } from "@burdenoff/fe-libs/ui";
10
11
  //#region src/buildmyiq/components/TutorPanel.tsx
11
- function h(e) {
12
+ function _(e) {
12
13
  let t = e.trim();
13
14
  return t ? t[0].toUpperCase() : "?";
14
15
  }
15
- var g = {
16
+ var v = {
16
17
  STRUGGLING: "Extra step-by-step help — based on your recent scores",
17
18
  ON_TRACK: "Standard depth",
18
19
  ADVANCED: "Deeper dive — based on your recent scores"
19
20
  };
20
- function _(e) {
21
+ function y(e) {
21
22
  if (!e) return "You";
22
23
  let t = [e.firstName, e.lastName].filter(Boolean).join(" ").trim();
23
24
  return e.name || t || e.username || e.email || "You";
24
25
  }
25
- function v({ lessonId: v, lessonTitle: y }) {
26
- let { currentUser: b } = e(), x = n(v), S = r(), [C, w] = o(void 0), [T, E] = o(""), [D, O] = o(null), k = a(null), A = x.data?.[0], j = C ? x.data?.find((e) => e.id === C)?.messages ?? [] : A?.messages ?? [];
27
- i(() => {
28
- k.current?.scrollTo({
29
- top: k.current.scrollHeight,
26
+ function b({ lessonId: b, lessonTitle: x, lessonTitlesById: S }) {
27
+ let { currentUser: C } = e(), w = t(), T = r(b), E = i(), [D, O] = s(void 0), [k, A] = s(""), [j, M] = s(null), N = o(null), P = T.data?.[0], F = D ? T.data?.find((e) => e.id === D)?.messages ?? [] : P?.messages ?? [];
28
+ a(() => {
29
+ N.current?.scrollTo({
30
+ top: N.current.scrollHeight,
30
31
  behavior: "smooth"
31
32
  });
32
- }, [j.length]);
33
- let M = _(b), N = async () => {
34
- let e = T.trim();
33
+ }, [F.length]);
34
+ let I = y(C), L = (e) => e === b ? x : S?.get(e) ?? e, R = async () => {
35
+ let e = k.trim();
35
36
  if (e) {
36
- O(null), E("");
37
+ M(null), A("");
37
38
  try {
38
- w((await S.mutateAsync({
39
- lessonId: v,
39
+ O((await E.mutateAsync({
40
+ lessonId: b,
40
41
  question: e,
41
- sessionId: C ?? A?.id
42
+ sessionId: D ?? P?.id
42
43
  })).session.id);
43
44
  } catch (t) {
44
- O(t instanceof Error ? t.message : "The AI tutor is temporarily unavailable."), E(e);
45
+ M(t instanceof Error ? t.message : "The AI tutor is temporarily unavailable."), A(e);
45
46
  }
46
47
  }
47
48
  };
48
- return /* @__PURE__ */ d(p, {
49
+ return /* @__PURE__ */ p(h, {
49
50
  treatment: "plain",
50
51
  className: "flex flex-col overflow-hidden p-0",
51
52
  children: [
52
- /* @__PURE__ */ d("div", {
53
+ /* @__PURE__ */ p("div", {
53
54
  className: "flex items-center gap-2 border-b border-border-seam px-4 py-3",
54
- children: [/* @__PURE__ */ u("span", {
55
+ children: [/* @__PURE__ */ f("span", {
55
56
  className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]",
56
- children: /* @__PURE__ */ u(l, { className: "h-4 w-4 text-[var(--color-accent-teal)]" })
57
- }), /* @__PURE__ */ d("div", {
57
+ children: /* @__PURE__ */ f(d, { className: "h-4 w-4 text-[var(--color-accent-teal)]" })
58
+ }), /* @__PURE__ */ p("div", {
58
59
  className: "min-w-0",
59
- children: [/* @__PURE__ */ u("p", {
60
+ children: [/* @__PURE__ */ f("p", {
60
61
  className: "text-sm font-semibold text-text-primary",
61
62
  children: "AI Tutor"
62
- }), /* @__PURE__ */ d("p", {
63
+ }), /* @__PURE__ */ p("p", {
63
64
  className: "truncate text-xs text-text-secondary",
64
65
  children: [
65
66
  "Grounded in “",
66
- y,
67
+ x,
67
68
  "”"
68
69
  ]
69
70
  })]
70
71
  })]
71
72
  }),
72
- /* @__PURE__ */ u("div", {
73
- ref: k,
73
+ /* @__PURE__ */ f("div", {
74
+ ref: N,
74
75
  className: "max-h-96 min-h-40 flex-1 overflow-y-auto px-4 py-4",
75
- children: x.isLoading ? /* @__PURE__ */ u(t, { label: "Loading your tutor conversation…" }) : j.length === 0 ? /* @__PURE__ */ d("div", {
76
+ children: T.isLoading ? /* @__PURE__ */ f(n, { label: "Loading your tutor conversation…" }) : F.length === 0 ? /* @__PURE__ */ p("div", {
76
77
  className: "flex flex-col items-center gap-2 py-6 text-center",
77
- children: [/* @__PURE__ */ u(s, { className: "h-8 w-8 text-text-tertiary" }), /* @__PURE__ */ u("p", {
78
+ children: [/* @__PURE__ */ f(l, { className: "h-8 w-8 text-text-tertiary" }), /* @__PURE__ */ f("p", {
78
79
  className: "text-sm text-text-secondary",
79
80
  children: "Ask a question about this lesson — the tutor answers from the lesson content, with hints first."
80
81
  })]
81
- }) : /* @__PURE__ */ d("ul", {
82
+ }) : /* @__PURE__ */ p("ul", {
82
83
  className: "flex flex-col gap-4",
83
- children: [j.map((e) => {
84
+ children: [F.map((e) => {
84
85
  let t = e.role === "ASSISTANT";
85
- return /* @__PURE__ */ d("li", {
86
+ return /* @__PURE__ */ p("li", {
86
87
  className: "flex gap-3",
87
- children: [/* @__PURE__ */ u("div", {
88
+ children: [/* @__PURE__ */ f("div", {
88
89
  className: t ? "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]" : "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-sm font-semibold text-text-secondary",
89
- children: t ? /* @__PURE__ */ u(s, { className: "h-4 w-4 text-[var(--color-accent-teal)]" }) : h(M)
90
- }), /* @__PURE__ */ d("div", {
90
+ children: t ? /* @__PURE__ */ f(l, { className: "h-4 w-4 text-[var(--color-accent-teal)]" }) : _(I)
91
+ }), /* @__PURE__ */ p("div", {
91
92
  className: "min-w-0 flex-1",
92
- children: [/* @__PURE__ */ d("div", {
93
- className: "flex flex-wrap items-baseline gap-x-2",
94
- children: [/* @__PURE__ */ u("span", {
95
- className: "text-sm font-semibold text-text-primary",
96
- children: t ? "AI Tutor" : M
97
- }), t && e.adaptiveLevel && /* @__PURE__ */ u("span", {
98
- className: "text-xs text-text-tertiary",
99
- children: g[e.adaptiveLevel]
100
- })]
101
- }), /* @__PURE__ */ u("p", {
102
- className: "mt-0.5 whitespace-pre-wrap break-words text-sm text-text-secondary",
103
- children: e.content
104
- })]
93
+ children: [
94
+ /* @__PURE__ */ p("div", {
95
+ className: "flex flex-wrap items-baseline gap-x-2",
96
+ children: [/* @__PURE__ */ f("span", {
97
+ className: "text-sm font-semibold text-text-primary",
98
+ children: t ? "AI Tutor" : I
99
+ }), t && e.adaptiveLevel && /* @__PURE__ */ f("span", {
100
+ className: "text-xs text-text-tertiary",
101
+ children: v[e.adaptiveLevel]
102
+ })]
103
+ }),
104
+ /* @__PURE__ */ f("p", {
105
+ className: "mt-0.5 whitespace-pre-wrap break-words text-sm text-text-secondary",
106
+ children: e.content
107
+ }),
108
+ t && e.groundingLessonIds.length > 0 && /* @__PURE__ */ p("div", {
109
+ className: "mt-2 flex flex-wrap items-center gap-1.5",
110
+ children: [/* @__PURE__ */ p("span", {
111
+ className: "inline-flex items-center gap-1 text-xs text-text-tertiary",
112
+ children: [/* @__PURE__ */ f(c, { className: "h-3.5 w-3.5" }), w("buildmyiq.tutor.groundedIn", "Answered from")]
113
+ }), e.groundingLessonIds.map((e) => /* @__PURE__ */ f("span", {
114
+ title: e,
115
+ className: "max-w-[14rem] truncate rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
116
+ children: L(e)
117
+ }, e))]
118
+ })
119
+ ]
105
120
  })]
106
121
  }, e.id);
107
- }), S.isPending && /* @__PURE__ */ d("li", {
122
+ }), E.isPending && /* @__PURE__ */ p("li", {
108
123
  className: "flex items-center gap-3",
109
- children: [/* @__PURE__ */ u("span", {
124
+ children: [/* @__PURE__ */ f("span", {
110
125
  className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]",
111
- children: /* @__PURE__ */ u(s, { className: "h-4 w-4 animate-pulse text-[var(--color-accent-teal)]" })
112
- }), /* @__PURE__ */ u("span", {
126
+ children: /* @__PURE__ */ f(l, { className: "h-4 w-4 animate-pulse text-[var(--color-accent-teal)]" })
127
+ }), /* @__PURE__ */ f("span", {
113
128
  className: "text-sm text-text-secondary",
114
129
  children: "Thinking…"
115
130
  })]
116
131
  })]
117
132
  })
118
133
  }),
119
- /* @__PURE__ */ d("div", {
134
+ /* @__PURE__ */ p("div", {
120
135
  className: "border-t border-border-seam p-3",
121
- children: [/* @__PURE__ */ u(m, {
122
- value: T,
123
- onChange: (e) => E(e.target.value),
136
+ children: [/* @__PURE__ */ f(g, {
137
+ value: k,
138
+ onChange: (e) => A(e.target.value),
124
139
  onKeyDown: (e) => {
125
- e.key === "Enter" && !e.shiftKey && (e.preventDefault(), N());
140
+ e.key === "Enter" && !e.shiftKey && (e.preventDefault(), R());
126
141
  },
127
142
  placeholder: "Ask about this lesson…",
128
143
  rows: 2,
129
- disabled: S.isPending
130
- }), /* @__PURE__ */ d("div", {
144
+ disabled: E.isPending
145
+ }), /* @__PURE__ */ p("div", {
131
146
  className: "mt-2 flex items-center justify-end gap-2",
132
- children: [D && /* @__PURE__ */ u("span", {
147
+ children: [j && /* @__PURE__ */ f("span", {
133
148
  className: "mr-auto text-xs text-status-error-text",
134
- children: D
135
- }), /* @__PURE__ */ d(f, {
149
+ children: j
150
+ }), /* @__PURE__ */ p(m, {
136
151
  size: "sm",
137
- onClick: N,
138
- disabled: !T.trim() || S.isPending,
139
- children: [/* @__PURE__ */ u(c, { className: "mr-1.5 h-4 w-4" }), S.isPending ? "Asking…" : "Ask"]
152
+ onClick: R,
153
+ disabled: !k.trim() || E.isPending,
154
+ children: [/* @__PURE__ */ f(u, { className: "mr-1.5 h-4 w-4" }), E.isPending ? "Asking…" : "Ask"]
140
155
  })]
141
156
  })]
142
157
  })
@@ -144,6 +159,6 @@ function v({ lessonId: v, lessonTitle: y }) {
144
159
  });
145
160
  }
146
161
  //#endregion
147
- export { v as TutorPanel };
162
+ export { b as TutorPanel };
148
163
 
149
164
  //# sourceMappingURL=TutorPanel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TutorPanel.js","names":[],"sources":["../../../src/buildmyiq/components/TutorPanel.tsx"],"sourcesContent":["/**\n * TutorPanel — the 24/7 AI tutor chat for a lesson (BOFF-4426).\n * @module buildmyiq/components/TutorPanel\n *\n * Grounded Q&A: every assistant answer comes from `buiTutorAsk`, which\n * retrieves the actual lesson content server-side before calling the model\n * (see `TutorService.retrieveGrounding` in wspace-buildmyiq-svc) — never an\n * ungrounded chat completion. Continues the learner's own session for this\n * lesson (multi-turn) via `useBuiMyTutorSessions` / `useBuiTutorAsk`.\n */\n\nimport { useEffect, useRef, useState } from 'react';\nimport { Bot, Send, Sparkles } from 'lucide-react';\nimport { Button, GlassCard, Textarea } from '@burdenoff/fe-libs/ui';\nimport { LoadingView } from './StateViews';\nimport { useBuiMyTutorSessions, useBuiTutorAsk } from '../hooks';\nimport { useBuildmyiq } from '../context';\nimport type { BuiTutorAdaptiveLevel, BuiTutorMessage } from '../types';\n\ninterface TutorPanelProps {\n lessonId: string;\n lessonTitle: string;\n}\n\nfunction initialFor(label: string): string {\n const trimmed = label.trim();\n return trimmed ? trimmed[0]!.toUpperCase() : '?';\n}\n\nconst ADAPTIVE_LABEL: Record<BuiTutorAdaptiveLevel, string> = {\n STRUGGLING: 'Extra step-by-step help — based on your recent scores',\n ON_TRACK: 'Standard depth',\n ADVANCED: 'Deeper dive — based on your recent scores',\n};\n\nfunction displayName(user: ReturnType<typeof useBuildmyiq>['currentUser']): string {\n if (!user) return 'You';\n const full = [user.firstName, user.lastName].filter(Boolean).join(' ').trim();\n return user.name || full || user.username || user.email || 'You';\n}\n\nexport function TutorPanel({ lessonId, lessonTitle }: TutorPanelProps) {\n const { currentUser } = useBuildmyiq();\n const sessionsQuery = useBuiMyTutorSessions(lessonId);\n const ask = useBuiTutorAsk();\n\n const [sessionId, setSessionId] = useState<string | undefined>(undefined);\n const [draft, setDraft] = useState('');\n const [error, setError] = useState<string | null>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n\n // Resume the learner's most recently active session for this lesson, if any.\n const existingSession = sessionsQuery.data?.[0];\n const messages: BuiTutorMessage[] = sessionId\n ? (sessionsQuery.data?.find((s) => s.id === sessionId)?.messages ?? [])\n : (existingSession?.messages ?? []);\n\n useEffect(() => {\n scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' });\n }, [messages.length]);\n\n const selfName = displayName(currentUser);\n\n const handleAsk = async () => {\n const question = draft.trim();\n if (!question) return;\n setError(null);\n setDraft('');\n try {\n const result = await ask.mutateAsync({\n lessonId,\n question,\n sessionId: sessionId ?? existingSession?.id,\n });\n setSessionId(result.session.id);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'The AI tutor is temporarily unavailable.');\n setDraft(question);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n void handleAsk();\n }\n };\n\n return (\n <GlassCard treatment=\"plain\" className=\"flex flex-col overflow-hidden p-0\">\n <div className=\"flex items-center gap-2 border-b border-border-seam px-4 py-3\">\n <span className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Sparkles className=\"h-4 w-4 text-[var(--color-accent-teal)]\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-semibold text-text-primary\">AI Tutor</p>\n <p className=\"truncate text-xs text-text-secondary\">Grounded in “{lessonTitle}”</p>\n </div>\n </div>\n\n <div ref={scrollRef} className=\"max-h-96 min-h-40 flex-1 overflow-y-auto px-4 py-4\">\n {sessionsQuery.isLoading ? (\n <LoadingView label=\"Loading your tutor conversation…\" />\n ) : messages.length === 0 ? (\n <div className=\"flex flex-col items-center gap-2 py-6 text-center\">\n <Bot className=\"h-8 w-8 text-text-tertiary\" />\n <p className=\"text-sm text-text-secondary\">\n Ask a question about this lesson — the tutor answers from the lesson content, with\n hints first.\n </p>\n </div>\n ) : (\n <ul className=\"flex flex-col gap-4\">\n {messages.map((message) => {\n const isAssistant = message.role === 'ASSISTANT';\n return (\n <li key={message.id} className=\"flex gap-3\">\n <div\n className={\n isAssistant\n ? 'flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]'\n : 'flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-sm font-semibold text-text-secondary'\n }\n >\n {isAssistant ? (\n <Bot className=\"h-4 w-4 text-[var(--color-accent-teal)]\" />\n ) : (\n initialFor(selfName)\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-baseline gap-x-2\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {isAssistant ? 'AI Tutor' : selfName}\n </span>\n {isAssistant && message.adaptiveLevel && (\n <span className=\"text-xs text-text-tertiary\">\n {ADAPTIVE_LABEL[message.adaptiveLevel]}\n </span>\n )}\n </div>\n <p className=\"mt-0.5 whitespace-pre-wrap break-words text-sm text-text-secondary\">\n {message.content}\n </p>\n </div>\n </li>\n );\n })}\n {ask.isPending && (\n <li className=\"flex items-center gap-3\">\n <span className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Bot className=\"h-4 w-4 animate-pulse text-[var(--color-accent-teal)]\" />\n </span>\n <span className=\"text-sm text-text-secondary\">Thinking…</span>\n </li>\n )}\n </ul>\n )}\n </div>\n\n <div className=\"border-t border-border-seam p-3\">\n <Textarea\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Ask about this lesson…\"\n rows={2}\n disabled={ask.isPending}\n />\n <div className=\"mt-2 flex items-center justify-end gap-2\">\n {error && <span className=\"mr-auto text-xs text-status-error-text\">{error}</span>}\n <Button size=\"sm\" onClick={handleAsk} disabled={!draft.trim() || ask.isPending}>\n <Send className=\"mr-1.5 h-4 w-4\" />\n {ask.isPending ? 'Asking…' : 'Ask'}\n </Button>\n </div>\n </div>\n </GlassCard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TutorPanel.js","names":[],"sources":["../../../src/buildmyiq/components/TutorPanel.tsx"],"sourcesContent":["/**\n * TutorPanel — the 24/7 AI tutor chat for a lesson (BOFF-4426).\n * @module buildmyiq/components/TutorPanel\n *\n * Grounded Q&A: every assistant answer comes from `buiTutorAsk`, which\n * retrieves the actual lesson content server-side before calling the model\n * (see `TutorService.retrieveGrounding` in wspace-buildmyiq-svc) — never an\n * ungrounded chat completion. Continues the learner's own session for this\n * lesson (multi-turn) via `useBuiMyTutorSessions` / `useBuiTutorAsk`.\n *\n * Each assistant turn carries the `groundingLessonIds` the answer was actually\n * derived from; they are rendered as source chips so the learner can see which\n * lesson content backs the answer instead of taking it on trust. Ids the caller\n * cannot name are shown as-is rather than guessed at or hidden.\n */\n\nimport { useEffect, useRef, useState } from 'react';\nimport { Bot, BookOpen, Send, Sparkles } from 'lucide-react';\nimport { Button, GlassCard, Textarea } from '@burdenoff/fe-libs/ui';\nimport { LoadingView } from './StateViews';\nimport { useBuiMyTutorSessions, useBuiTutorAsk } from '../hooks';\nimport { useBuildmyiq } from '../context';\nimport { useTr } from '../shared/hooks/useTr';\nimport type { BuiTutorAdaptiveLevel, BuiTutorMessage } from '../types';\n\ninterface TutorPanelProps {\n lessonId: string;\n lessonTitle: string;\n /**\n * Titles for the lessons the tutor may ground an answer in, keyed by lesson\n * id — supplied by the host page, which has already loaded the course. Only\n * used to LABEL the ids the server returned; a missing entry falls back to\n * the raw id, never to an invented title.\n */\n lessonTitlesById?: ReadonlyMap<string, string>;\n}\n\nfunction initialFor(label: string): string {\n const trimmed = label.trim();\n return trimmed ? trimmed[0]!.toUpperCase() : '?';\n}\n\nconst ADAPTIVE_LABEL: Record<BuiTutorAdaptiveLevel, string> = {\n STRUGGLING: 'Extra step-by-step help — based on your recent scores',\n ON_TRACK: 'Standard depth',\n ADVANCED: 'Deeper dive — based on your recent scores',\n};\n\nfunction displayName(user: ReturnType<typeof useBuildmyiq>['currentUser']): string {\n if (!user) return 'You';\n const full = [user.firstName, user.lastName].filter(Boolean).join(' ').trim();\n return user.name || full || user.username || user.email || 'You';\n}\n\nexport function TutorPanel({ lessonId, lessonTitle, lessonTitlesById }: TutorPanelProps) {\n const { currentUser } = useBuildmyiq();\n const tr = useTr();\n const sessionsQuery = useBuiMyTutorSessions(lessonId);\n const ask = useBuiTutorAsk();\n\n const [sessionId, setSessionId] = useState<string | undefined>(undefined);\n const [draft, setDraft] = useState('');\n const [error, setError] = useState<string | null>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n\n // Resume the learner's most recently active session for this lesson, if any.\n const existingSession = sessionsQuery.data?.[0];\n const messages: BuiTutorMessage[] = sessionId\n ? (sessionsQuery.data?.find((s) => s.id === sessionId)?.messages ?? [])\n : (existingSession?.messages ?? []);\n\n useEffect(() => {\n scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' });\n }, [messages.length]);\n\n const selfName = displayName(currentUser);\n\n /** Label a grounding lesson id; an id we can't name is shown verbatim. */\n const groundingLabel = (id: string): string => {\n if (id === lessonId) return lessonTitle;\n return lessonTitlesById?.get(id) ?? id;\n };\n\n const handleAsk = async () => {\n const question = draft.trim();\n if (!question) return;\n setError(null);\n setDraft('');\n try {\n const result = await ask.mutateAsync({\n lessonId,\n question,\n sessionId: sessionId ?? existingSession?.id,\n });\n setSessionId(result.session.id);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'The AI tutor is temporarily unavailable.');\n setDraft(question);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n void handleAsk();\n }\n };\n\n return (\n <GlassCard treatment=\"plain\" className=\"flex flex-col overflow-hidden p-0\">\n <div className=\"flex items-center gap-2 border-b border-border-seam px-4 py-3\">\n <span className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Sparkles className=\"h-4 w-4 text-[var(--color-accent-teal)]\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-semibold text-text-primary\">AI Tutor</p>\n <p className=\"truncate text-xs text-text-secondary\">Grounded in “{lessonTitle}”</p>\n </div>\n </div>\n\n <div ref={scrollRef} className=\"max-h-96 min-h-40 flex-1 overflow-y-auto px-4 py-4\">\n {sessionsQuery.isLoading ? (\n <LoadingView label=\"Loading your tutor conversation…\" />\n ) : messages.length === 0 ? (\n <div className=\"flex flex-col items-center gap-2 py-6 text-center\">\n <Bot className=\"h-8 w-8 text-text-tertiary\" />\n <p className=\"text-sm text-text-secondary\">\n Ask a question about this lesson — the tutor answers from the lesson content, with\n hints first.\n </p>\n </div>\n ) : (\n <ul className=\"flex flex-col gap-4\">\n {messages.map((message) => {\n const isAssistant = message.role === 'ASSISTANT';\n return (\n <li key={message.id} className=\"flex gap-3\">\n <div\n className={\n isAssistant\n ? 'flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]'\n : 'flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-sm font-semibold text-text-secondary'\n }\n >\n {isAssistant ? (\n <Bot className=\"h-4 w-4 text-[var(--color-accent-teal)]\" />\n ) : (\n initialFor(selfName)\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-baseline gap-x-2\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {isAssistant ? 'AI Tutor' : selfName}\n </span>\n {isAssistant && message.adaptiveLevel && (\n <span className=\"text-xs text-text-tertiary\">\n {ADAPTIVE_LABEL[message.adaptiveLevel]}\n </span>\n )}\n </div>\n <p className=\"mt-0.5 whitespace-pre-wrap break-words text-sm text-text-secondary\">\n {message.content}\n </p>\n {isAssistant && message.groundingLessonIds.length > 0 && (\n <div className=\"mt-2 flex flex-wrap items-center gap-1.5\">\n <span className=\"inline-flex items-center gap-1 text-xs text-text-tertiary\">\n <BookOpen className=\"h-3.5 w-3.5\" />\n {tr('buildmyiq.tutor.groundedIn', 'Answered from')}\n </span>\n {message.groundingLessonIds.map((groundingId) => (\n <span\n key={groundingId}\n title={groundingId}\n className=\"max-w-[14rem] truncate rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {groundingLabel(groundingId)}\n </span>\n ))}\n </div>\n )}\n </div>\n </li>\n );\n })}\n {ask.isPending && (\n <li className=\"flex items-center gap-3\">\n <span className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Bot className=\"h-4 w-4 animate-pulse text-[var(--color-accent-teal)]\" />\n </span>\n <span className=\"text-sm text-text-secondary\">Thinking…</span>\n </li>\n )}\n </ul>\n )}\n </div>\n\n <div className=\"border-t border-border-seam p-3\">\n <Textarea\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Ask about this lesson…\"\n rows={2}\n disabled={ask.isPending}\n />\n <div className=\"mt-2 flex items-center justify-end gap-2\">\n {error && <span className=\"mr-auto text-xs text-status-error-text\">{error}</span>}\n <Button size=\"sm\" onClick={handleAsk} disabled={!draft.trim() || ask.isPending}>\n <Send className=\"mr-1.5 h-4 w-4\" />\n {ask.isPending ? 'Asking…' : 'Ask'}\n </Button>\n </div>\n </div>\n </GlassCard>\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { BuiConnection, BuiCourse, BuiCourseLevel, BuiCourseStatus, BuiModule, BuiLesson, BuiAssessment, BuiAssessmentStatus, BuiQuestion, BuiSubmission, BuiSubmissionStatus, BuiEnrollment, BuiEnrollmentStatus, BuiCohort, BuiCohortStatus, BuiSkill, BuiSkillCategory, BuiSkillStandard, BuiCertification, BuiSkillMappingSuggestion, BuiCertificate, BuiLessonProgress, BuiCreateCourseInput, BuiUpdateCourseInput, BuiCreateModuleInput, BuiCreateLessonInput, BuiCreateAssessmentInput, BuiCreateQuestionInput, BuiCreateSkillInput, BuiCreateCohortInput, BuiEnrollInput, BuiRecordLessonProgressInput, BuiSubmitAssessmentInput, BuiGradeSubmissionInput, BuiIssueCertificateInput, BuiTutorAskInput, BuiTutorAskPayload, BuiTutorSession, BuiGradeSuggestion, BuiCourseOutlineSuggestion, BuiApplyCourseOutlineInput, BuiAdminCopilotAnswer, BuiAdminCopilotTurnInput, BuiAiSuggestionOutcome, BuiLtiPlatform, BuiRegisterLtiPlatformInput } from '../types';
1
+ import { BuiConnection, BuiCourse, BuiCourseLevel, BuiCourseStatus, BuiModule, BuiLesson, BuiAssessment, BuiAssessmentStatus, BuiQuestion, BuiSubmission, BuiSubmissionStatus, BuiEnrollment, BuiEnrollmentStatus, BuiCohort, BuiCohortStatus, BuiSkill, BuiSkillCategory, BuiSkillStandard, BuiCertification, BuiSkillMappingSuggestion, BuiCertificate, BuiLessonProgress, BuiCreateCourseInput, BuiUpdateCourseInput, BuiCreateModuleInput, BuiCreateLessonInput, BuiCreateAssessmentInput, BuiCreateQuestionInput, BuiCreateSkillInput, BuiCreateCohortInput, BuiEnrollInput, BuiRecordLessonProgressInput, BuiSubmitAssessmentInput, BuiGradeSubmissionInput, BuiIssueCertificateInput, BuiTutorAskInput, BuiTutorAskPayload, BuiTutorSession, BuiGradeSuggestion, BuiNextLessonSuggestion, BuiCourseOutlineSuggestion, BuiApplyCourseOutlineInput, BuiAdminCopilotAnswer, BuiAdminCopilotTurnInput, BuiAiSuggestionOutcome, BuiLtiPlatform, BuiRegisterLtiPlatformInput } from '../types';
2
2
  export interface UseBuiCoursesArgs {
3
3
  level?: BuiCourseLevel;
4
4
  status?: BuiCourseStatus;
@@ -107,6 +107,16 @@ export declare const useBuiGradeSubmission: () => import('@tanstack/react-query'
107
107
  id: string;
108
108
  input: BuiGradeSubmissionInput;
109
109
  }, unknown>;
110
+ /**
111
+ * Deterministically grades a submission whose assessment is made up ENTIRELY of
112
+ * objective questions (SINGLE_CHOICE / MULTIPLE_CHOICE / TRUE_FALSE) by
113
+ * comparing the learner's answers against each question's answer key — no LLM
114
+ * and no human judgement involved. Unlike `useBuiSuggestGrade` this DOES write
115
+ * the submission. The svc throws when the assessment contains any
116
+ * SHORT_ANSWER / ESSAY / CODE question; callers gate the action on the
117
+ * assessment's question types and surface the error honestly if it still fails.
118
+ */
119
+ export declare const useBuiAutoScoreSubmission: () => import('@tanstack/react-query').UseMutationResult<BuiSubmission, Error, string, unknown>;
110
120
  export interface UseBuiEnrollmentsArgs {
111
121
  courseId?: string;
112
122
  status?: BuiEnrollmentStatus;
@@ -123,6 +133,14 @@ export declare const useBuiEnrollments: (args?: UseBuiEnrollmentsArgs) => import
123
133
  /** The signed-in learner's own enrollments (self-scoped, no learnerId arg). */
124
134
  export declare const useBuiMyEnrollments: (args?: UseBuiEnrollmentsArgs) => import('@tanstack/react-query').UseQueryResult<NoInfer<BuiConnection<BuiEnrollment>>, Error>;
125
135
  export declare const useBuiEnrollment: (id: string | undefined) => import('@tanstack/react-query').UseQueryResult<NoInfer<BuiEnrollment | null>, Error>;
136
+ /**
137
+ * The signed-in learner's OWN enrollment by id, with its full lesson-progress
138
+ * history. Self-scoped (`buiMyEnrollment`) — the resolver owner-filters by the
139
+ * authenticated actor and returns null for someone else's enrollment, so this
140
+ * is the read a learner-facing surface must use; `useBuiEnrollment` above is
141
+ * the workspace-scoped instructor/admin equivalent.
142
+ */
143
+ export declare const useBuiMyEnrollment: (id: string | undefined) => import('@tanstack/react-query').UseQueryResult<NoInfer<BuiEnrollment | null>, Error>;
126
144
  export declare const useBuiEnroll: () => import('@tanstack/react-query').UseMutationResult<BuiEnrollment, Error, BuiEnrollInput, unknown>;
127
145
  export declare const useBuiTransitionEnrollment: () => import('@tanstack/react-query').UseMutationResult<BuiEnrollment, Error, {
128
146
  id: string;
@@ -174,6 +192,21 @@ export declare const useBuiReviewGradeSuggestion: () => import('@tanstack/react-
174
192
  id: string;
175
193
  outcome: BuiAiSuggestionOutcome;
176
194
  }, unknown>;
195
+ /**
196
+ * Proposes the lesson the learner should take next in one of their own
197
+ * enrollments — a deterministic curriculum-order "resume where you left off"
198
+ * walk plus a pacing hint derived from their recent graded scores (not an LLM
199
+ * call). It persists an auditable suggestion record but NEVER writes
200
+ * `BuiLessonProgress` / `BuiEnrollment`, so acting on it is entirely the
201
+ * learner's choice. Type-B self-scoped: `enrollmentId` is a client arg but the
202
+ * resolver owner-filters by the authenticated actor.
203
+ */
204
+ export declare const useBuiSuggestNextLesson: () => import('@tanstack/react-query').UseMutationResult<BuiNextLessonSuggestion, Error, string, unknown>;
205
+ /** Records whether the learner accepted or dismissed a next-lesson suggestion. */
206
+ export declare const useBuiReviewNextLessonSuggestion: () => import('@tanstack/react-query').UseMutationResult<BuiNextLessonSuggestion, Error, {
207
+ id: string;
208
+ outcome: BuiAiSuggestionOutcome;
209
+ }, unknown>;
177
210
  /**
178
211
  * AI-drafted course outline from a topic — an EPHEMERAL suggestion (not
179
212
  * persisted server-side); the instructor reviews/edits it before
@@ -1 +1 @@
1
- {"version":3,"file":"useBuildmyiqApi.d.ts","sourceRoot":"","sources":["../../../src/buildmyiq/hooks/useBuildmyiqApi.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useBuildmyiqApi.d.ts","sourceRoot":"","sources":["../../../src/buildmyiq/hooks/useBuildmyiqApi.ts"],"names":[],"mappings":"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"}