coursecode 0.1.28 → 0.1.31
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.
- package/framework/css/components/flip-cards.css +14 -0
- package/framework/docs/USER_GUIDE.md +1 -1
- package/lib/mcp-prompts.js +0 -61
- package/lib/mcp-server.js +2 -12
- package/lib/stub-player/edit-mode.js +10 -0
- package/lib/stub-player/outline-mode.js +48 -0
- package/lib/stub-player/styles/_outline-mode.css +110 -0
- package/package.json +1 -1
|
@@ -47,6 +47,20 @@
|
|
|
47
47
|
height: 100%; /* Fill the entire grid cell height (for equal row heights) */
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/* When multiple flip cards sit side-by-side in any grid or flex container,
|
|
51
|
+
align back-face content to the top so headings line up across cards.
|
|
52
|
+
:has(> .flip-card ~ .flip-card) matches any parent containing 2+ flip cards. */
|
|
53
|
+
:has(> .flip-card ~ .flip-card) > .flip-card .flip-card-back {
|
|
54
|
+
justify-content: flex-start;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Back-face content stretches to fill the card and uses space-between
|
|
58
|
+
so the heading sits at top and remaining content anchors to bottom */
|
|
59
|
+
:has(> .flip-card ~ .flip-card) > .flip-card .flip-card-back .flip-card-content {
|
|
60
|
+
flex: 1;
|
|
61
|
+
justify-content: space-between;
|
|
62
|
+
}
|
|
63
|
+
|
|
50
64
|
.flip-card-inner {
|
|
51
65
|
display: grid; /* Grid allows both faces to contribute to height */
|
|
52
66
|
width: 100%;
|
|
@@ -302,7 +302,7 @@ Once connected, your AI assistant gains these capabilities:
|
|
|
302
302
|
| `coursecode_interaction_catalog` | Browse available interaction types (multiple choice, drag-drop, etc.) |
|
|
303
303
|
| `coursecode_css_catalog` | Browse available CSS classes by category |
|
|
304
304
|
| `coursecode_icon_catalog` | Browse available icons by name/category |
|
|
305
|
-
|
|
305
|
+
|
|
306
306
|
| `coursecode_workflow_status` | Get guidance on what to do next based on your project's current state |
|
|
307
307
|
| `coursecode_build` | Build the course for LMS deployment |
|
|
308
308
|
|
package/lib/mcp-prompts.js
CHANGED
|
@@ -407,67 +407,7 @@ Use to discover available icons before authoring slides or configuring menus.`,
|
|
|
407
407
|
idempotentHint: true
|
|
408
408
|
}
|
|
409
409
|
},
|
|
410
|
-
{
|
|
411
|
-
name: 'coursecode_export_content',
|
|
412
|
-
description: `Extract course content as structured Markdown or JSON for review.
|
|
413
|
-
|
|
414
|
-
Returns the full text content of the course: slide headers, body text, tabs, accordions, callouts, cards, interactions, assessment questions, narration, config, and structure overview.
|
|
415
|
-
|
|
416
|
-
Use cases:
|
|
417
|
-
- Compare built course against COURSE_OUTLINE.md for accuracy
|
|
418
|
-
- Review all interactions and assessment questions at once
|
|
419
|
-
- Audit content wording and consistency across slides
|
|
420
|
-
- Generate content for localization or SME review
|
|
421
|
-
|
|
422
|
-
Filtering options keep output manageable:
|
|
423
|
-
- slides: scope to specific slide IDs
|
|
424
|
-
- interactionsOnly: just Q&A, no slide content
|
|
425
|
-
- excludeInteractions: content only, no Q&A
|
|
426
|
-
- format: 'md' (default) or 'json' for structured data
|
|
427
410
|
|
|
428
|
-
For large courses (>40KB), full exports automatically return a summary with per-slide sizes instead. Use the slides parameter to export specific content.
|
|
429
|
-
|
|
430
|
-
Does not require preview server.`,
|
|
431
|
-
inputSchema: {
|
|
432
|
-
type: 'object',
|
|
433
|
-
properties: {
|
|
434
|
-
slides: {
|
|
435
|
-
type: 'string',
|
|
436
|
-
description: 'Comma-separated slide IDs to export (default: all slides)'
|
|
437
|
-
},
|
|
438
|
-
interactionsOnly: {
|
|
439
|
-
type: 'boolean',
|
|
440
|
-
description: 'Export only interactions and assessment questions (no slide content)'
|
|
441
|
-
},
|
|
442
|
-
includeNarration: {
|
|
443
|
-
type: 'boolean',
|
|
444
|
-
description: 'Include narration transcripts (default: false)'
|
|
445
|
-
},
|
|
446
|
-
includeAnswers: {
|
|
447
|
-
type: 'boolean',
|
|
448
|
-
description: 'Include correct answers for interactions (default: true)'
|
|
449
|
-
},
|
|
450
|
-
includeFeedback: {
|
|
451
|
-
type: 'boolean',
|
|
452
|
-
description: 'Include feedback text (default: true)'
|
|
453
|
-
},
|
|
454
|
-
excludeInteractions: {
|
|
455
|
-
type: 'boolean',
|
|
456
|
-
description: 'Exclude all interactions from output (default: false)'
|
|
457
|
-
},
|
|
458
|
-
format: {
|
|
459
|
-
type: 'string',
|
|
460
|
-
description: 'Output format: md or json (default: md)',
|
|
461
|
-
enum: ['md', 'json']
|
|
462
|
-
}
|
|
463
|
-
},
|
|
464
|
-
required: []
|
|
465
|
-
},
|
|
466
|
-
annotations: {
|
|
467
|
-
readOnlyHint: true,
|
|
468
|
-
idempotentHint: true
|
|
469
|
-
}
|
|
470
|
-
},
|
|
471
411
|
];
|
|
472
412
|
|
|
473
413
|
// ========================================
|
|
@@ -692,7 +632,6 @@ Visually verify and polish the course using the preview server.
|
|
|
692
632
|
- coursecode_navigate — go to any slide by ID (get IDs from coursecode_state)
|
|
693
633
|
- coursecode_screenshot — capture visual state (accepts slideId to navigate+capture in one call)
|
|
694
634
|
- coursecode_interact — test interactions with responses
|
|
695
|
-
- coursecode_export_content — extract all text content to review or compare against outline
|
|
696
635
|
- coursecode_lint — validate after file changes
|
|
697
636
|
|
|
698
637
|
4. Efficient iteration loop:
|
package/lib/mcp-server.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
lintCourse,
|
|
31
31
|
buildCourse
|
|
32
32
|
} from './authoring-api.js';
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
import headless from './headless-browser.js';
|
|
35
35
|
import { TOOLS, buildInstructions, getWorkflowStatusWithInstructions } from './mcp-prompts.js';
|
|
36
36
|
|
|
@@ -258,17 +258,7 @@ export async function startMcpServer(options = {}) {
|
|
|
258
258
|
result = await lintCourse();
|
|
259
259
|
break;
|
|
260
260
|
|
|
261
|
-
|
|
262
|
-
result = await getContentExport(args || {});
|
|
263
|
-
if (result === null) {
|
|
264
|
-
throw new Error('Failed to export content. Ensure course-config.js exists in course/');
|
|
265
|
-
}
|
|
266
|
-
return {
|
|
267
|
-
content: [{
|
|
268
|
-
type: 'text',
|
|
269
|
-
text: result
|
|
270
|
-
}]
|
|
271
|
-
};
|
|
261
|
+
|
|
272
262
|
|
|
273
263
|
default:
|
|
274
264
|
throw new Error(`Unknown tool: ${name}`);
|
|
@@ -129,6 +129,16 @@ export function createEditModeHandlers(context) {
|
|
|
129
129
|
outline-offset: 2px;
|
|
130
130
|
background-color: rgba(245, 158, 11, 0.08);
|
|
131
131
|
}
|
|
132
|
+
.edit-mode-active .flip-card,
|
|
133
|
+
.edit-mode-active .accordion-button,
|
|
134
|
+
.edit-mode-active [data-action="select-tab"],
|
|
135
|
+
.edit-mode-active [data-action="toggle-collapse"],
|
|
136
|
+
.edit-mode-active [data-component="modal-trigger"],
|
|
137
|
+
.edit-mode-active .timeline-event,
|
|
138
|
+
.edit-mode-active [data-component="lightbox"],
|
|
139
|
+
.edit-mode-active [data-action="toggle-dropdown"] {
|
|
140
|
+
pointer-events: none;
|
|
141
|
+
}
|
|
132
142
|
`;
|
|
133
143
|
doc.head.appendChild(styleEl);
|
|
134
144
|
}
|
|
@@ -41,6 +41,7 @@ export function createOutlineModeHandlers(context) {
|
|
|
41
41
|
let isVisible = false;
|
|
42
42
|
let courseLoaded = false;
|
|
43
43
|
let viewingStage = null; // Which stage the dashboard is showing
|
|
44
|
+
const calloutDismissed = localStorage.getItem('coursecode-walkthroughCalloutDismissed') === 'true';
|
|
44
45
|
|
|
45
46
|
async function checkStage() {
|
|
46
47
|
const params = new URLSearchParams(window.location.search);
|
|
@@ -133,6 +134,9 @@ export function createOutlineModeHandlers(context) {
|
|
|
133
134
|
// Stage stepper
|
|
134
135
|
parts.push(renderStepper(detectedStage, viewingStage));
|
|
135
136
|
|
|
137
|
+
// Walkthrough callout (dismissable, shown below stepper)
|
|
138
|
+
parts.push(renderWalkthroughCallout());
|
|
139
|
+
|
|
136
140
|
// Stage header — contextual button text
|
|
137
141
|
const hasSlides = stageData.checklist?.hasSlides;
|
|
138
142
|
let skipLabel;
|
|
@@ -511,6 +515,39 @@ export function createOutlineModeHandlers(context) {
|
|
|
511
515
|
`;
|
|
512
516
|
}
|
|
513
517
|
|
|
518
|
+
// ── Walkthrough Callout ───────────────────────────────────
|
|
519
|
+
|
|
520
|
+
function renderWalkthroughCallout() {
|
|
521
|
+
if (calloutDismissed) return '';
|
|
522
|
+
|
|
523
|
+
const hasSlides = stageData?.checklist?.hasSlides;
|
|
524
|
+
let skipLabel;
|
|
525
|
+
if (courseLoaded) {
|
|
526
|
+
skipLabel = '← Back to Course';
|
|
527
|
+
} else if (hasSlides) {
|
|
528
|
+
skipLabel = 'View Course ▸';
|
|
529
|
+
} else {
|
|
530
|
+
skipLabel = 'Skip to Course ▸';
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return `
|
|
534
|
+
<div class="outline-walkthrough-callout" id="outline-walkthrough-callout">
|
|
535
|
+
<div class="outline-callout-content">
|
|
536
|
+
<div class="outline-callout-icon">💡</div>
|
|
537
|
+
<div class="outline-callout-text">
|
|
538
|
+
<strong>This is an optional walkthrough.</strong>
|
|
539
|
+
It guides you through building a course step-by-step, but you can skip it at any time.
|
|
540
|
+
Close with the <span class="outline-callout-kbd">✕</span> in the top-right corner, or return anytime from the course preview toolbar.
|
|
541
|
+
</div>
|
|
542
|
+
</div>
|
|
543
|
+
<div class="outline-callout-actions">
|
|
544
|
+
<button id="outline-callout-skip-btn" class="outline-callout-skip-btn">${skipLabel}</button>
|
|
545
|
+
<button id="outline-callout-dismiss-btn" class="outline-callout-dismiss-btn">Got it</button>
|
|
546
|
+
</div>
|
|
547
|
+
</div>
|
|
548
|
+
`;
|
|
549
|
+
}
|
|
550
|
+
|
|
514
551
|
// ── Shared Components ────────────────────────────────────
|
|
515
552
|
|
|
516
553
|
function renderChecklist() {
|
|
@@ -587,6 +624,17 @@ export function createOutlineModeHandlers(context) {
|
|
|
587
624
|
// Skip / Back button
|
|
588
625
|
document.getElementById('stub-player-skip-outline-btn')?.addEventListener('click', dismissDashboard);
|
|
589
626
|
|
|
627
|
+
// Walkthrough callout handlers
|
|
628
|
+
document.getElementById('outline-callout-skip-btn')?.addEventListener('click', dismissDashboard);
|
|
629
|
+
document.getElementById('outline-callout-dismiss-btn')?.addEventListener('click', () => {
|
|
630
|
+
localStorage.setItem('coursecode-walkthroughCalloutDismissed', 'true');
|
|
631
|
+
const callout = document.getElementById('outline-walkthrough-callout');
|
|
632
|
+
if (callout) {
|
|
633
|
+
callout.classList.add('dismissing');
|
|
634
|
+
callout.addEventListener('animationend', () => callout.remove(), { once: true });
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
|
|
590
638
|
// Stepper clicks
|
|
591
639
|
outlineContent.querySelectorAll('.stepper-step').forEach(btn => {
|
|
592
640
|
btn.addEventListener('click', () => {
|
|
@@ -147,6 +147,116 @@
|
|
|
147
147
|
margin-top: 20px;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/* ── Walkthrough Callout ────────────────── */
|
|
151
|
+
|
|
152
|
+
.outline-walkthrough-callout {
|
|
153
|
+
margin-bottom: 24px;
|
|
154
|
+
padding: 16px 20px;
|
|
155
|
+
background: rgba(74, 111, 165, 0.1);
|
|
156
|
+
border: 1px solid rgba(74, 111, 165, 0.25);
|
|
157
|
+
border-left: 3px solid var(--color-info);
|
|
158
|
+
border-radius: 8px;
|
|
159
|
+
animation: callout-slide-in 0.3s ease-out;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@keyframes callout-slide-in {
|
|
163
|
+
from { opacity: 0; transform: translateY(-8px); }
|
|
164
|
+
to { opacity: 1; transform: translateY(0); }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.outline-walkthrough-callout.dismissing {
|
|
168
|
+
animation: callout-slide-out 0.25s ease-in forwards;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@keyframes callout-slide-out {
|
|
172
|
+
from { opacity: 1; transform: translateY(0); max-height: 200px; margin-bottom: 24px; }
|
|
173
|
+
to { opacity: 0; transform: translateY(-8px); max-height: 0; margin-bottom: 0; padding: 0 20px; overflow: hidden; }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.outline-callout-content {
|
|
177
|
+
display: flex;
|
|
178
|
+
gap: 12px;
|
|
179
|
+
align-items: flex-start;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.outline-callout-icon {
|
|
183
|
+
font-size: 20px;
|
|
184
|
+
line-height: 1;
|
|
185
|
+
flex-shrink: 0;
|
|
186
|
+
margin-top: 1px;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.outline-callout-text {
|
|
190
|
+
font-size: 13px;
|
|
191
|
+
line-height: 1.6;
|
|
192
|
+
color: var(--color-gray-400);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.outline-callout-text strong {
|
|
196
|
+
color: var(--color-white);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.outline-callout-kbd {
|
|
200
|
+
display: inline-flex;
|
|
201
|
+
align-items: center;
|
|
202
|
+
justify-content: center;
|
|
203
|
+
width: 18px;
|
|
204
|
+
height: 18px;
|
|
205
|
+
background: rgba(0, 0, 0, 0.3);
|
|
206
|
+
border: 1px solid var(--color-primary-panel);
|
|
207
|
+
border-radius: 4px;
|
|
208
|
+
font-size: 10px;
|
|
209
|
+
color: var(--color-gray-400);
|
|
210
|
+
vertical-align: middle;
|
|
211
|
+
line-height: 1;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.outline-callout-actions {
|
|
215
|
+
display: flex;
|
|
216
|
+
gap: 8px;
|
|
217
|
+
margin-top: 14px;
|
|
218
|
+
padding-left: 32px;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.outline-callout-skip-btn {
|
|
222
|
+
background: var(--color-info);
|
|
223
|
+
color: var(--color-white);
|
|
224
|
+
border: none;
|
|
225
|
+
padding: 7px 16px;
|
|
226
|
+
border-radius: 6px;
|
|
227
|
+
font-size: 12px;
|
|
228
|
+
font-weight: 600;
|
|
229
|
+
cursor: pointer;
|
|
230
|
+
transition: background 0.15s, transform 0.1s;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.outline-callout-skip-btn:hover {
|
|
234
|
+
background: color-mix(in srgb, var(--color-info) 80%, var(--color-white));
|
|
235
|
+
transform: translateY(-1px);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.outline-callout-skip-btn:active {
|
|
239
|
+
transform: translateY(0);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.outline-callout-dismiss-btn {
|
|
243
|
+
background: none;
|
|
244
|
+
color: var(--color-gray-600);
|
|
245
|
+
border: 1px solid var(--color-primary-panel);
|
|
246
|
+
padding: 7px 14px;
|
|
247
|
+
border-radius: 6px;
|
|
248
|
+
font-size: 12px;
|
|
249
|
+
font-weight: 500;
|
|
250
|
+
cursor: pointer;
|
|
251
|
+
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.outline-callout-dismiss-btn:hover {
|
|
255
|
+
background: var(--color-primary-panel);
|
|
256
|
+
color: var(--color-gray-200);
|
|
257
|
+
border-color: var(--color-info);
|
|
258
|
+
}
|
|
259
|
+
|
|
150
260
|
/* ── Stage Header ───────────────────────── */
|
|
151
261
|
|
|
152
262
|
.outline-stage-header {
|