@academy-sdk/sdk 0.1.0 → 0.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 (102) hide show
  1. package/dist/bundle.js +70 -0
  2. package/dist/manifest.json +5 -0
  3. package/dist/styles.css +3307 -0
  4. package/package.json +40 -46
  5. package/src/components/atoms/Avatar.tsx +38 -0
  6. package/src/components/atoms/Badge.tsx +32 -0
  7. package/src/components/atoms/Button.tsx +48 -0
  8. package/src/components/atoms/Card.tsx +33 -0
  9. package/src/components/atoms/Input.tsx +39 -0
  10. package/src/components/atoms/ProgressBar.tsx +52 -0
  11. package/src/components/atoms/Tabs.tsx +47 -0
  12. package/src/components/atoms/index.ts +10 -0
  13. package/src/components/index.ts +11 -0
  14. package/src/components/molecules/CourseCard.tsx +215 -0
  15. package/src/components/molecules/EmptyState.tsx +23 -0
  16. package/src/components/molecules/LoadingSpinner.tsx +27 -0
  17. package/src/components/molecules/PageHeader.tsx +22 -0
  18. package/src/components/molecules/Pagination.tsx +82 -0
  19. package/src/components/molecules/SearchInput.tsx +35 -0
  20. package/src/components/molecules/index.ts +12 -0
  21. package/src/components/organisms/CourseSidebar.tsx +276 -0
  22. package/src/components/organisms/LearnerNavbar.tsx +129 -0
  23. package/src/components/organisms/LearnerSidebar.tsx +148 -0
  24. package/src/components/organisms/LessonBookmarks.tsx +128 -0
  25. package/src/components/organisms/LessonNotes.tsx +153 -0
  26. package/src/components/organisms/index.ts +10 -0
  27. package/src/components/pages/BundleDetailPage.tsx +388 -0
  28. package/src/components/pages/CatalogBundlesPage.tsx +96 -0
  29. package/src/components/pages/CatalogCoursesPage.tsx +299 -0
  30. package/src/components/pages/CourseDetailPage.tsx +582 -0
  31. package/src/components/pages/CoursePlayerPage.tsx +481 -0
  32. package/src/components/pages/CreatorProfilePage.tsx +161 -0
  33. package/src/components/pages/LearnerSettingsPage.tsx +58 -0
  34. package/src/components/pages/ManualReviewDetailPage.tsx +254 -0
  35. package/src/components/pages/ManualReviewPage.tsx +228 -0
  36. package/src/components/pages/MessagesPage.tsx +285 -0
  37. package/src/components/pages/MyLearningPage.tsx +239 -0
  38. package/src/components/pages/PaymentCancelPage.tsx +74 -0
  39. package/src/components/pages/PaymentSuccessPage.tsx +73 -0
  40. package/src/components/pages/index.ts +13 -0
  41. package/src/components/utils.ts +6 -0
  42. package/src/contracts/components.contract.ts +89 -0
  43. package/src/contracts/index.ts +4 -0
  44. package/src/contracts/layout.contract.ts +36 -0
  45. package/src/contracts/pages.contract.ts +275 -0
  46. package/src/contracts/template.contract.ts +100 -0
  47. package/src/default-template.tsx +52 -0
  48. package/src/hooks/index.ts +28 -0
  49. package/src/hooks/sdk-context.tsx +152 -0
  50. package/src/hooks/useAiCoach.ts +27 -0
  51. package/src/hooks/useBookmarks.ts +35 -0
  52. package/src/hooks/useCourseSearch.ts +18 -0
  53. package/src/hooks/useDebounce.ts +19 -0
  54. package/src/hooks/useMyBundles.ts +21 -0
  55. package/src/hooks/useMyCourses.ts +20 -0
  56. package/src/hooks/useNotes.ts +35 -0
  57. package/src/hooks/useNotifications.ts +16 -0
  58. package/src/hooks/useTheme.ts +17 -0
  59. package/src/hooks/useToast.ts +17 -0
  60. package/src/hooks/useUser.ts +33 -0
  61. package/src/index.ts +33 -0
  62. package/src/layouts/DefaultLayout.tsx +58 -0
  63. package/src/manifest.json +5 -0
  64. package/src/styles.css +43 -0
  65. package/src/types/ai-coach.ts +25 -0
  66. package/src/types/bookmarks.ts +20 -0
  67. package/src/types/bundle.ts +119 -0
  68. package/src/types/common.ts +24 -0
  69. package/src/types/course.ts +135 -0
  70. package/src/types/enrollment.ts +35 -0
  71. package/src/types/index.ts +15 -0
  72. package/src/types/lesson.ts +106 -0
  73. package/src/types/manual-review.ts +116 -0
  74. package/src/types/messaging.ts +109 -0
  75. package/src/types/notification.ts +30 -0
  76. package/src/types/payment.ts +40 -0
  77. package/src/types/progress.ts +19 -0
  78. package/src/types/rating.ts +20 -0
  79. package/src/types/search.ts +31 -0
  80. package/src/types/user.ts +16 -0
  81. package/src/utils/formatters.ts +74 -0
  82. package/src/utils/index.ts +8 -0
  83. package/dist/components/atoms/index.cjs +0 -318
  84. package/dist/components/atoms/index.js +0 -288
  85. package/dist/components/index.cjs +0 -1275
  86. package/dist/components/index.js +0 -1245
  87. package/dist/components/molecules/index.cjs +0 -334
  88. package/dist/components/molecules/index.js +0 -311
  89. package/dist/components/organisms/index.cjs +0 -855
  90. package/dist/components/organisms/index.js +0 -825
  91. package/dist/components/pages/index.cjs +0 -3306
  92. package/dist/components/pages/index.js +0 -3315
  93. package/dist/contracts/index.cjs +0 -52
  94. package/dist/contracts/index.js +0 -29
  95. package/dist/hooks/index.cjs +0 -165
  96. package/dist/hooks/index.js +0 -142
  97. package/dist/index.cjs +0 -630
  98. package/dist/index.js +0 -600
  99. package/dist/types/index.cjs +0 -18
  100. package/dist/types/index.js +0 -0
  101. package/dist/utils/index.cjs +0 -80
  102. package/dist/utils/index.js +0 -57
@@ -0,0 +1,152 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext, type ReactNode } from 'react';
4
+ import type { User, Membership } from '../types';
5
+
6
+ /**
7
+ * SDK Hook implementations provided by the platform.
8
+ * Template developers don't interact with this directly — they use the individual hooks.
9
+ */
10
+ export interface SDKHookImplementations {
11
+ // User & Auth
12
+ useUser: () => { user: User | null; isAuthenticated: boolean; memberships: Membership[] };
13
+ useLogout: () => { logout: () => void };
14
+
15
+ // My Learning
16
+ useMyCourses: (status?: string, page?: number, limit?: number) => {
17
+ courses: any[];
18
+ meta: any | null;
19
+ isLoading: boolean;
20
+ };
21
+ useMyBundles: (status?: string, page?: number, limit?: number, search?: string) => {
22
+ bundles: any[];
23
+ meta: any | null;
24
+ isLoading: boolean;
25
+ };
26
+
27
+ // Search
28
+ useCourseSearch: (query: string) => {
29
+ results: any | null;
30
+ isSearching: boolean;
31
+ };
32
+
33
+ // Notes
34
+ useLessonNotes: (activityId: string) => {
35
+ data: any[];
36
+ isLoading: boolean;
37
+ error: Error | null;
38
+ refetch: () => Promise<void>;
39
+ };
40
+ useCreateNote: () => {
41
+ createNote: (payload: any) => Promise<any>;
42
+ isLoading: boolean;
43
+ };
44
+ useUpdateNote: () => {
45
+ updateNote: (noteId: string, payload: any) => Promise<any>;
46
+ isLoading: boolean;
47
+ };
48
+ useDeleteNote: () => {
49
+ deleteNote: (noteId: string) => Promise<boolean>;
50
+ isLoading: boolean;
51
+ };
52
+
53
+ // Bookmarks
54
+ useLessonBookmarks: (activityId: string) => {
55
+ data: any[];
56
+ isLoading: boolean;
57
+ error: Error | null;
58
+ refetch: () => Promise<void>;
59
+ };
60
+ useCreateBookmark: () => {
61
+ createBookmark: (payload: any) => Promise<any>;
62
+ isLoading: boolean;
63
+ };
64
+ useUpdateBookmark: () => {
65
+ updateBookmark: (id: string, payload: any) => Promise<any>;
66
+ isLoading: boolean;
67
+ };
68
+ useDeleteBookmark: () => {
69
+ deleteBookmark: (id: string) => Promise<boolean>;
70
+ isLoading: boolean;
71
+ };
72
+
73
+ // AI Coach
74
+ useAiCoachAvailability: (courseId?: string | null) => {
75
+ loading: boolean;
76
+ active: boolean;
77
+ coach: any | null;
78
+ error: Error | null;
79
+ };
80
+ useAiCoachChat: (courseId: string | null | undefined) => {
81
+ messages: any[];
82
+ threadId: string | null;
83
+ isStreaming: boolean;
84
+ error: Error | null;
85
+ sendMessage: (text: string) => Promise<void>;
86
+ stopStreaming: () => void;
87
+ };
88
+ useAiCoachHistory: (courseId: string | null | undefined, enabled: boolean) => {
89
+ loading: boolean;
90
+ threadId: string | null;
91
+ messages: any[];
92
+ groupedHistory: any[];
93
+ hasMore: boolean;
94
+ loadMore: () => Promise<void>;
95
+ };
96
+
97
+ // Notifications
98
+ useNotifications: () => {
99
+ notifications: any[];
100
+ unreadCount: number;
101
+ isLoading: boolean;
102
+ isLoadingMore: boolean;
103
+ hasMore: boolean;
104
+ loadMore: () => void;
105
+ refresh: () => void;
106
+ markConversationRead: (conversationId: string) => Promise<void>;
107
+ };
108
+
109
+ // Theme
110
+ useTheme: () => {
111
+ theme: string;
112
+ setTheme: (theme: string) => void;
113
+ };
114
+
115
+ // Debounce utility hook
116
+ useDebounce: <T>(value: T, delay: number) => T;
117
+
118
+ // Toast notifications
119
+ useToast: () => {
120
+ toast: (props: { title?: string; description?: string; variant?: 'default' | 'destructive' }) => void;
121
+ };
122
+ }
123
+
124
+ const SDKContext = createContext<SDKHookImplementations | null>(null);
125
+
126
+ export interface SDKProviderProps {
127
+ implementations: SDKHookImplementations;
128
+ children: ReactNode;
129
+ }
130
+
131
+ /**
132
+ * SDKProvider wraps learner routes and provides hook implementations.
133
+ * Used by the platform internally — template developers don't need this.
134
+ */
135
+ export function SDKProvider({ implementations, children }: SDKProviderProps) {
136
+ return <SDKContext.Provider value={implementations}>{children}</SDKContext.Provider>;
137
+ }
138
+
139
+ /**
140
+ * Internal hook to access SDK implementations.
141
+ * @internal
142
+ */
143
+ export function useSDK(): SDKHookImplementations {
144
+ const ctx = useContext(SDKContext);
145
+ if (!ctx) {
146
+ throw new Error(
147
+ 'useSDK must be used within an SDKProvider. ' +
148
+ 'Make sure your learner routes are wrapped with <SDKProvider>.'
149
+ );
150
+ }
151
+ return ctx;
152
+ }
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Check if AI Coach is available for a course.
7
+ */
8
+ export function useAiCoachAvailability(courseId?: string | null) {
9
+ const sdk = useSDK();
10
+ return sdk.useAiCoachAvailability(courseId);
11
+ }
12
+
13
+ /**
14
+ * Chat with the AI Coach.
15
+ */
16
+ export function useAiCoachChat(courseId: string | null | undefined) {
17
+ const sdk = useSDK();
18
+ return sdk.useAiCoachChat(courseId);
19
+ }
20
+
21
+ /**
22
+ * Get AI Coach chat history.
23
+ */
24
+ export function useAiCoachHistory(courseId: string | null | undefined, enabled: boolean) {
25
+ const sdk = useSDK();
26
+ return sdk.useAiCoachHistory(courseId, enabled);
27
+ }
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Fetch bookmarks for a lesson/activity.
7
+ */
8
+ export function useLessonBookmarks(activityId: string) {
9
+ const sdk = useSDK();
10
+ return sdk.useLessonBookmarks(activityId);
11
+ }
12
+
13
+ /**
14
+ * Create a new bookmark.
15
+ */
16
+ export function useCreateBookmark() {
17
+ const sdk = useSDK();
18
+ return sdk.useCreateBookmark();
19
+ }
20
+
21
+ /**
22
+ * Update an existing bookmark.
23
+ */
24
+ export function useUpdateBookmark() {
25
+ const sdk = useSDK();
26
+ return sdk.useUpdateBookmark();
27
+ }
28
+
29
+ /**
30
+ * Delete a bookmark.
31
+ */
32
+ export function useDeleteBookmark() {
33
+ const sdk = useSDK();
34
+ return sdk.useDeleteBookmark();
35
+ }
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Search enrolled courses.
7
+ *
8
+ * @param query - Search query string
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * const { results, isSearching } = useCourseSearch(debouncedQuery);
13
+ * ```
14
+ */
15
+ export function useCourseSearch(query: string) {
16
+ const sdk = useSDK();
17
+ return sdk.useCourseSearch(query);
18
+ }
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Debounce a value.
7
+ *
8
+ * @param value - The value to debounce
9
+ * @param delay - Delay in milliseconds
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const debouncedSearch = useDebounce(searchQuery, 500);
14
+ * ```
15
+ */
16
+ export function useDebounce<T>(value: T, delay: number): T {
17
+ const sdk = useSDK();
18
+ return sdk.useDebounce(value, delay);
19
+ }
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Fetch the current user's enrolled bundles.
7
+ *
8
+ * @param status - Filter by status: 'IN_PROGRESS' | 'COMPLETED' | undefined (all)
9
+ * @param page - Page number (1-based)
10
+ * @param limit - Items per page (default: 12)
11
+ * @param search - Optional search query
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const { bundles, meta, isLoading } = useMyBundles(undefined, 1, 12);
16
+ * ```
17
+ */
18
+ export function useMyBundles(status?: string, page?: number, limit?: number, search?: string) {
19
+ const sdk = useSDK();
20
+ return sdk.useMyBundles(status, page, limit, search);
21
+ }
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Fetch the current user's enrolled courses.
7
+ *
8
+ * @param status - Filter by status: 'IN_PROGRESS' | 'COMPLETED' | undefined (all)
9
+ * @param page - Page number (1-based)
10
+ * @param limit - Items per page (default: 12)
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const { courses, meta, isLoading } = useMyCourses('IN_PROGRESS', 1, 12);
15
+ * ```
16
+ */
17
+ export function useMyCourses(status?: string, page?: number, limit?: number) {
18
+ const sdk = useSDK();
19
+ return sdk.useMyCourses(status, page, limit);
20
+ }
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Fetch notes for a lesson/activity.
7
+ */
8
+ export function useLessonNotes(activityId: string) {
9
+ const sdk = useSDK();
10
+ return sdk.useLessonNotes(activityId);
11
+ }
12
+
13
+ /**
14
+ * Create a new note.
15
+ */
16
+ export function useCreateNote() {
17
+ const sdk = useSDK();
18
+ return sdk.useCreateNote();
19
+ }
20
+
21
+ /**
22
+ * Update an existing note.
23
+ */
24
+ export function useUpdateNote() {
25
+ const sdk = useSDK();
26
+ return sdk.useUpdateNote();
27
+ }
28
+
29
+ /**
30
+ * Delete a note.
31
+ */
32
+ export function useDeleteNote() {
33
+ const sdk = useSDK();
34
+ return sdk.useDeleteNote();
35
+ }
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Manage notifications (message notifications).
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { notifications, unreadCount, markConversationRead } = useNotifications();
11
+ * ```
12
+ */
13
+ export function useNotifications() {
14
+ const sdk = useSDK();
15
+ return sdk.useNotifications();
16
+ }
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Access and change the current theme.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { theme, setTheme } = useTheme();
11
+ * return <button onClick={() => setTheme('dark')}>Dark Mode</button>;
12
+ * ```
13
+ */
14
+ export function useTheme() {
15
+ const sdk = useSDK();
16
+ return sdk.useTheme();
17
+ }
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Show toast notifications.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { toast } = useToast();
11
+ * toast({ title: 'Success', description: 'Course enrolled!' });
12
+ * ```
13
+ */
14
+ export function useToast() {
15
+ const sdk = useSDK();
16
+ return sdk.useToast();
17
+ }
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ import { useSDK } from './sdk-context';
4
+
5
+ /**
6
+ * Get the current authenticated user.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { user, isAuthenticated } = useUser();
11
+ * if (user) {
12
+ * return <span>Hello, {user.name}</span>;
13
+ * }
14
+ * ```
15
+ */
16
+ export function useUser() {
17
+ const sdk = useSDK();
18
+ return sdk.useUser();
19
+ }
20
+
21
+ /**
22
+ * Get the logout function.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * const { logout } = useLogout();
27
+ * return <button onClick={logout}>Log out</button>;
28
+ * ```
29
+ */
30
+ export function useLogout() {
31
+ const sdk = useSDK();
32
+ return sdk.useLogout();
33
+ }
package/src/index.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @academy/sdk
3
+ *
4
+ * SDK for building custom learner templates for the Academy platform.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * import { useMyCourses, useUser } from '@academy/sdk';
9
+ * import type { MyLearningPageProps, TemplateManifest } from '@academy/sdk';
10
+ * ```
11
+ */
12
+
13
+ // Types
14
+ export * from './types';
15
+
16
+ // Hooks
17
+ export * from './hooks';
18
+
19
+ // Contracts
20
+ export * from './contracts';
21
+
22
+ // Utilities
23
+ export * from './utils';
24
+
25
+ // Layouts
26
+ export { DefaultLayout } from './layouts/DefaultLayout';
27
+
28
+ // Components are available via sub-path imports:
29
+ // import { Button, Card } from '@academy/sdk/components/atoms';
30
+ // import { CourseCard, Pagination } from '@academy/sdk/components/molecules';
31
+ // import { LearnerNavbar, LearnerSidebar } from '@academy/sdk/components/organisms';
32
+ // import { MyLearningPage, CatalogCoursesPage } from '@academy/sdk/components/pages';
33
+ // Or import all: import { Button, CourseCard } from '@academy/sdk/components';
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+
3
+ import { useState, type ReactNode } from 'react';
4
+ import { LearnerNavbar } from '../components/organisms/LearnerNavbar';
5
+ import { LearnerSidebar } from '../components/organisms/LearnerSidebar';
6
+
7
+ interface DefaultLayoutProps {
8
+ children: ReactNode;
9
+ currentPath?: string;
10
+ onNavigate?: (path: string) => void;
11
+ }
12
+
13
+ export function DefaultLayout({ children, currentPath = '/my-learning', onNavigate }: DefaultLayoutProps) {
14
+ const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
15
+ const [sidebarOpen, setSidebarOpen] = useState(false);
16
+
17
+ return (
18
+ <div className="min-h-screen bg-theme-bg-primary">
19
+ {/* Fixed Navbar */}
20
+ <div style={{ position: 'fixed', top: 'var(--preview-toolbar-h, 0px)', left: 0, right: 0, zIndex: 40 }}>
21
+ <LearnerNavbar
22
+ onMenuClick={() => {
23
+ if (typeof window !== 'undefined' && window.innerWidth >= 1024) {
24
+ setSidebarCollapsed(c => !c);
25
+ } else {
26
+ setSidebarOpen(o => !o);
27
+ }
28
+ }}
29
+ />
30
+ </div>
31
+
32
+ {/* Content below navbar (h-20 = 80px) + preview toolbar offset */}
33
+ <div style={{ paddingTop: 'calc(80px + var(--preview-toolbar-h, 0px))', display: 'flex', minHeight: '100vh' }}>
34
+ {/* Sidebar */}
35
+ <LearnerSidebar
36
+ currentPath={currentPath}
37
+ isCollapsed={sidebarCollapsed}
38
+ isOpen={sidebarOpen}
39
+ onClose={() => setSidebarOpen(false)}
40
+ onToggle={() => setSidebarCollapsed(c => !c)}
41
+ onNavigate={onNavigate ?? (() => {})}
42
+ />
43
+
44
+ {/* Main content */}
45
+ <main
46
+ style={{
47
+ flex: 1,
48
+ minWidth: 0,
49
+ transition: 'margin-left 0.3s',
50
+ }}
51
+ className="px-4 sm:px-6 lg:px-8 py-6"
52
+ >
53
+ {children}
54
+ </main>
55
+ </div>
56
+ </div>
57
+ );
58
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@academy-sdk/sdk",
3
+ "version": "0.2.0",
4
+ "description": "SDK for building custom learner templates for the Academy platform"
5
+ }
package/src/styles.css ADDED
@@ -0,0 +1,43 @@
1
+ @import "tailwindcss";
2
+
3
+ /* Tell Tailwind v4 where to scan for class names */
4
+ @source "../src/**/*.{tsx,ts,js,jsx}";
5
+
6
+ /* Mirror the host app's @theme mappings so custom Tailwind utilities work */
7
+ @theme {
8
+ /* Background colors */
9
+ --color-theme-bg-primary: rgb(var(--bg-primary));
10
+ --color-theme-bg-secondary: rgb(var(--bg-secondary));
11
+ --color-theme-bg-tertiary: rgb(var(--bg-tertiary));
12
+
13
+ /* Text colors */
14
+ --color-theme-text-primary: rgb(var(--text-primary));
15
+ --color-theme-text-secondary: rgb(var(--text-secondary));
16
+ --color-theme-text-muted: rgb(var(--text-muted));
17
+
18
+ /* Border colors */
19
+ --color-theme-border-primary: rgb(var(--border-primary));
20
+
21
+ /* Accent colors */
22
+ --color-theme-accent-primary: rgb(var(--accent-primary));
23
+ --color-theme-accent-secondary: rgb(var(--accent-secondary));
24
+
25
+ /* Marketing success stats gradient */
26
+ --color-theme-success-gradient-from: rgb(var(--success-gradient-from));
27
+ --color-theme-success-gradient-to: rgb(var(--success-gradient-to));
28
+ }
29
+
30
+ /* Default (light) theme — fallback when host doesn't inject theme variables */
31
+ :root {
32
+ --bg-primary: 249 250 251;
33
+ --bg-secondary: 255 255 255;
34
+ --bg-tertiary: 243 244 246;
35
+ --text-primary: 17 24 39;
36
+ --text-secondary: 75 85 99;
37
+ --text-muted: 156 163 175;
38
+ --border-primary: 229 231 235;
39
+ --accent-primary: 73 187 189;
40
+ --accent-secondary: 73 187 189;
41
+ --success-gradient-from: 19 108 181;
42
+ --success-gradient-to: 73 187 189;
43
+ }
@@ -0,0 +1,25 @@
1
+ export type ChatRole = 'user' | 'assistant';
2
+
3
+ export interface ChatMessage {
4
+ id: string;
5
+ role: ChatRole;
6
+ content: string;
7
+ createdAt: number;
8
+ isStreaming?: boolean;
9
+ sources?: unknown[];
10
+ }
11
+
12
+ export interface AICoachInfo {
13
+ id: string;
14
+ name: string;
15
+ }
16
+
17
+ export interface AICoachAvailability {
18
+ active: boolean;
19
+ coach: AICoachInfo | null;
20
+ }
21
+
22
+ export interface AICoachHistoryGroup {
23
+ date: string;
24
+ messages: ChatMessage[];
25
+ }
@@ -0,0 +1,20 @@
1
+ export interface Bookmark {
2
+ id: string;
3
+ activityId: string;
4
+ userId: string;
5
+ second: number;
6
+ label?: string;
7
+ createdAt: string;
8
+ updatedAt: string;
9
+ }
10
+
11
+ export interface CreateBookmarkPayload {
12
+ activityId: string;
13
+ second: number;
14
+ label?: string;
15
+ }
16
+
17
+ export interface UpdateBookmarkPayload {
18
+ second?: number;
19
+ label?: string;
20
+ }