-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathtutorials.js
More file actions
480 lines (419 loc) · 16.4 KB
/
tutorials.js
File metadata and controls
480 lines (419 loc) · 16.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
// =============================================================================
// CARROT TUTORIALS SYSTEM 🥕
// Interactive tutorial system with step-by-step guides
// =============================================================================
import { CarrotDebug } from './debugger.js';
import { extension_settings } from '../../../extensions.js';
import { EXTENSION_NAME } from './carrot-state.js';
// Use consistent extension name from carrot-state.js
const extensionName = EXTENSION_NAME;
// Tutorial system state
let currentTutorial = null;
let currentStep = 0;
let tutorialSteps = [];
let resizeHandler = null;
// Tutorial definitions - 5 comprehensive tutorials with 40+ steps total
const tutorials = {
'basic-setup': {
title: 'System Configuration Tutorial',
steps: [
{
target: '.carrot-setting-item:first-child',
title: 'Master Enable',
content: `
Turn on CarrotKernel. Must be enabled for all functionality.
`
},
{
target: '.carrot-setting-item:nth-child(2)',
title: 'AI Injection',
content: `
Send character data to AI automatically when characters are mentioned.
`
},
{
target: '.carrot-setting-item:nth-child(3)',
title: 'Display Mode',
content: `
How character data appears in chats:
No Display (recommended), Thinking Box, or Character Cards
`
},
{
target: '.carrot-search-container',
title: 'Search Lorebooks',
content: `
Type to filter your lorebook list quickly.
`
},
{
target: '.carrot-lorebook-item',
title: 'Select Lorebooks',
content: `
Check boxes next to lorebooks you want to use.
`
},
{
target: '#carrot-scan-btn',
title: 'Scan Selected',
content: `
Click to scan and index character data from selected lorebooks.
`
}
]
},
'repository-management': {
title: 'Repository Management Tutorial',
steps: [
{
target: 'button#carrot-scan-btn',
title: 'Start Here: Scan Button',
content: `
🔍 CLICK "SCAN SELECTED LOREBOOKS" to begin!
This scans your lorebooks for character data and creates a searchable repository.
After scanning, you'll see character cards that you can click to view details.
✨ The scan finds <BunnymoTags> blocks and organizes character information automatically.
`
},
{
target: '#carrot-lorebook-management',
title: 'Two Types of Lorebooks',
content: `
👤 CHARACTER REPOSITORIES: Contain individual character data
📚 TAG LIBRARIES: Contain tag definitions (species, personality, etc.)
You need both types for complete functionality.
`
},
{
target: '.carrot-lorebook-item',
title: 'Mark Repository Types',
content: `
Use the 👤/📚 buttons to mark lorebook types.
Character repos have <BunnymoTags> blocks with character names.
Tag libraries have definitions like "TSUNDERE: Hostile but caring..."
`
}
]
},
'injection-system': {
title: 'AI Injection System Tutorial',
steps: [
{
target: '.carrot-status-injection',
title: 'How Injection Works',
content: `
When you mention "Alice" in chat:
1. CarrotKernel detects the character name
2. Sends Alice's data to AI context
3. AI maintains character consistency
4. Your chat stays clean (ephemeral injection)
`
},
{
target: 'select#carrot_display_mode',
title: 'Display Modes',
content: `
Choose how character data appears:
NO DISPLAY: Silent injection (recommended)
THINKING BOX: Shows in expandable boxes
CHARACTER CARDS: Visual character cards
`
},
{
target: 'input#carrot_injection_depth',
title: 'Injection Depth',
content: `
Controls priority in AI context.
Depth 4 (recommended): Same as GuidedGenerations
Lower = higher priority but may interfere
Higher = lower priority, may be ignored
`
}
]
},
'template-editor': {
title: 'Template Editor Tutorial',
steps: [
{
target: 'select#bmt_template_selector',
title: 'Select Template',
content: `
Choose which template to edit. Templates control how character data is formatted for the AI.
`
},
{
target: 'textarea#prompt',
title: 'Edit Template Content',
content: `
Write your injection prompt using {{MACRO_NAME}} variables:
{{TRIGGERED_CHARACTER_TAGS}} - Character data
{{CHARACTER_LIST}} - Character names
`
},
{
target: '.bmt-button-group',
title: 'Template Actions',
content: `
👁️ Preview: See template with real data
💾 Save: Save your changes
📋 Duplicate: Copy template for experiments
`
}
]
}
};
// Tutorial launcher methods
export function openSystemTutorial() {
CarrotDebug.ui('🎓 openSystemTutorial called');
startTutorial('basic-setup');
}
export function openRepositoryTutorial() {
CarrotDebug.ui('🎓 openRepositoryTutorial called');
startTutorial('repository-management');
}
export function openInjectionTutorial() {
CarrotDebug.ui('🎓 openInjectionTutorial called');
startTutorial('injection-system');
}
export function openTemplateEditorTutorial() {
startTutorial('template-editor');
}
// Start a tutorial by ID - using confirm() dialogs
export async function startTutorial(tutorialId) {
CarrotDebug.ui(`🎓 startTutorial called with ID: ${tutorialId}`);
const tutorial = tutorials[tutorialId];
if (!tutorial) {
alert(`Tutorial "${tutorialId}" not found`);
return;
}
const steps = tutorial.steps;
// Show each step with browser confirm dialog
for (let i = 0; i < steps.length; i++) {
const step = steps[i];
// Highlight the target element
const target = document.querySelector(step.target);
if (target) {
// Remove previous highlights
document.querySelectorAll('.carrot-tutorial-highlight')
.forEach(el => el.classList.remove('carrot-tutorial-highlight'));
// Add highlight to current target
target.classList.add('carrot-tutorial-highlight');
// Scroll to target
const isMobile = window.innerWidth <= 768 || 'ontouchstart' in window;
const scrollOptions = {
behavior: 'smooth',
block: isMobile ? 'start' : 'center',
inline: 'nearest'
};
target.scrollIntoView(scrollOptions);
// Wait for scroll to complete
await new Promise(resolve => setTimeout(resolve, isMobile ? 800 : 500));
}
// Clean up HTML tags and format text nicely
const cleanContent = step.content
.replace(/<[^>]*>/g, '') // Remove HTML tags
.replace(/\s+/g, ' ') // Normalize whitespace
.replace(/</g, '<').replace(/>/g, '>') // Fix HTML entities
.replace(/&/g, '&') // Fix ampersands
.trim();
// Format text with proper line breaks for readability
const formattedContent = cleanContent
.replace(/([.!?])\s+([A-Z])/g, '$1\n\n$2') // Add breaks after sentences
.replace(/([:])\s*([A-Z•])/g, '$1\n$2') // Add breaks after colons
.replace(/•\s/g, '\n• ') // Put bullets on new lines
.replace(/(\d+\.)\s/g, '\n$1 ') // Put numbered items on new lines
.replace(/\n\n\n+/g, '\n\n') // Clean up multiple line breaks
.trim();
// Show step as confirm dialog
const continueClicked = confirm(
`Step ${i + 1} of ${steps.length}: ${step.title}\n\n${formattedContent}\n\nClick OK for next step, Cancel to exit tutorial.`
);
if (!continueClicked) {
break; // User cancelled
}
}
// Clean up highlights
document.querySelectorAll('.carrot-tutorial-highlight')
.forEach(el => el.classList.remove('carrot-tutorial-highlight'));
alert('Tutorial completed! 🎉');
}
// Show current tutorial step
export function showTutorialStep() {
CarrotDebug.ui(`🎓 showTutorialStep called, currentStep: ${currentStep}`);
if (!tutorialSteps || tutorialSteps.length === 0) {
CarrotDebug.error('⚠️ No tutorial steps, closing');
closeTutorial();
return;
}
const step = tutorialSteps[currentStep];
const tutorial = tutorials[currentTutorial];
CarrotDebug.ui(`🎓 Looking for target element: ${step.target}`);
// Find target element
const targetElement = document.querySelector(step.target);
if (!targetElement) {
CarrotDebug.error(`⚠️ Tutorial target not found: ${step.target}, skipping step`);
CarrotDebug.ui(`⚠️ Tutorial target not found: ${step.target}, skipping step`);
// Try next step
if (currentStep < tutorialSteps.length - 1) {
currentStep++;
showTutorialStep();
} else {
closeTutorial();
}
return;
}
CarrotDebug.ui(`✅ Found target element, showing tutorial overlay`);
// Highlight the target element
highlightElement(targetElement, step);
// Show tutorial overlay with step content
showTutorialOverlay(tutorial, step, targetElement);
}
// Highlight target element
function highlightElement(element, step) {
// Remove existing highlights
document.querySelectorAll('.carrot-tutorial-highlight').forEach(el => {
el.classList.remove('carrot-tutorial-highlight');
});
// Add highlight to target
element.classList.add('carrot-tutorial-highlight');
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
// Show tutorial overlay
function showTutorialOverlay(tutorial, step, targetElement) {
CarrotDebug.ui(`🎓 showTutorialOverlay called`);
// Get or create overlay
let overlay = getTutorialOverlay();
CarrotDebug.ui(`🎓 Overlay element:`, overlay);
// Create tutorial popup content
const totalSteps = tutorialSteps.length;
const stepNumber = currentStep + 1;
const progress = ((currentStep + 1) / totalSteps) * 100;
const popupHTML = `
<div class="carrot-tutorial-popup" id="carrot-tutorial-popup">
<div class="carrot-tutorial-header">
<h3 class="carrot-tutorial-title">${tutorial.title}</h3>
<button class="carrot-tutorial-close" onclick="window.closeTutorial()">×</button>
</div>
<div class="carrot-tutorial-progress">
<div class="carrot-tutorial-progress-bar" style="width: ${progress}%"></div>
<span class="carrot-tutorial-progress-text">Step ${stepNumber} of ${totalSteps}</span>
</div>
<div class="carrot-tutorial-step-header">
<h4>${step.title}</h4>
</div>
<div class="carrot-tutorial-content">
${step.content}
</div>
<div class="carrot-tutorial-navigation">
${currentStep > 0 ? '<button class="carrot-tutorial-btn carrot-tutorial-prev" onclick="window.previousTutorialStep()">← Previous</button>' : '<span></span>'}
${currentStep < totalSteps - 1
? '<button class="carrot-tutorial-btn carrot-tutorial-next" onclick="window.nextTutorialStep()">Next →</button>'
: '<button class="carrot-tutorial-btn carrot-tutorial-finish" onclick="window.closeTutorial()">Finish</button>'}
</div>
</div>
`;
overlay.innerHTML = popupHTML;
overlay.style.display = 'flex';
overlay.classList.add('active'); // Make overlay visible
CarrotDebug.ui(`🎓 Set overlay display to flex and added active class`);
// Position popup near target
const targetRect = targetElement.getBoundingClientRect();
const popup = document.getElementById('carrot-tutorial-popup');
CarrotDebug.ui(`🎓 Tutorial popup element:`, popup);
positionTutorialPopup(popup, targetRect);
// Add resize handler
if (resizeHandler) {
window.removeEventListener('resize', resizeHandler);
}
resizeHandler = () => {
const newRect = targetElement.getBoundingClientRect();
positionTutorialPopup(popup, newRect);
};
window.addEventListener('resize', resizeHandler);
}
// Get or create tutorial overlay
function getTutorialOverlay() {
let overlay = document.getElementById('carrot-tutorial-overlay');
if (!overlay) {
overlay = document.createElement('div');
overlay.id = 'carrot-tutorial-overlay';
overlay.className = 'carrot-tutorial-overlay';
document.body.appendChild(overlay);
}
return overlay;
}
// Position tutorial popup near target element
function positionTutorialPopup(popup, targetRect) {
if (!popup) return;
const popupRect = popup.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// Try to position to the right of target
let left = targetRect.right + 20;
let top = targetRect.top;
// If it goes off right edge, position to the left
if (left + popupRect.width > viewportWidth - 20) {
left = targetRect.left - popupRect.width - 20;
}
// If still off screen, center horizontally
if (left < 20) {
left = (viewportWidth - popupRect.width) / 2;
}
// Adjust vertical position to keep in viewport
if (top + popupRect.height > viewportHeight - 20) {
top = viewportHeight - popupRect.height - 20;
}
if (top < 20) {
top = 20;
}
popup.style.left = Math.max(20, left) + 'px';
popup.style.top = Math.max(20, top) + 'px';
}
// Navigate to next tutorial step
export function nextTutorialStep() {
if (currentStep < tutorialSteps.length - 1) {
currentStep++;
showTutorialStep();
} else {
closeTutorial();
}
}
// Navigate to previous tutorial step
export function previousTutorialStep() {
if (currentStep > 0) {
currentStep--;
showTutorialStep();
}
}
// Close tutorial
export function closeTutorial() {
CarrotDebug.ui('Closing tutorial');
// Remove highlight
document.querySelectorAll('.carrot-tutorial-highlight').forEach(el => {
el.classList.remove('carrot-tutorial-highlight');
});
// Remove overlay
const overlay = document.getElementById('carrot-tutorial-overlay');
if (overlay) {
overlay.remove();
}
// Remove resize handler
if (resizeHandler) {
window.removeEventListener('resize', resizeHandler);
resizeHandler = null;
}
// Reset state
currentTutorial = null;
currentStep = 0;
tutorialSteps = [];
}
// Export navigation functions to window for onclick handlers
if (typeof window !== 'undefined') {
window.nextTutorialStep = nextTutorialStep;
window.previousTutorialStep = previousTutorialStep;
window.closeTutorial = closeTutorial;
}
// Initialize tutorial system
export function initializeTutorials() {
CarrotDebug.init('✅ Tutorials system initialized');
}