@amg555/frameforge-mcp 2.4.8 → 2.4.10
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/package.json +3 -3
- package/plugins/video-pipeline/skills/html-renderer/references/motion-craft.md +92 -0
- package/plugins/video-pipeline/skills/html-renderer/references/render-contract.md +66 -0
- package/plugins/video-pipeline/skills/video-pipeline/references/motion-craft.md +91 -0
- package/plugins/video-pipeline/skills/video-pipeline/references/render-contract.md +65 -0
- package/scripts/deep-validate.js +8 -2
- package/src/harness/bin/harness.mjs +32 -2
- package/src/harness/lib/verify-quality.mjs +252 -0
- package/src/mcp-server/server.js +227 -4
- package/src/video-renderer/renderer.js +7 -1
- package/src/video-renderer/scene-animator.js +1016 -10
- package/src/workflows/assets.js +4 -0
- package/src/workflows/render.js +2 -0
- package/src/workflows/script.js +18 -10
|
@@ -279,15 +279,147 @@ function detectAesthetic({
|
|
|
279
279
|
return 'cyber_tech';
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
// ── Anchor Requested Detector ───────────────────────────────────────────────
|
|
283
|
+
/** Returns true ONLY if caller explicitly asks for an on-screen anchor, presenter, host, avatar, or talking head in prompt. */
|
|
284
|
+
function detectAnchorRequested(text = '') {
|
|
285
|
+
if (!text || typeof text !== 'string') return false;
|
|
286
|
+
return /(?:with|show|display|include|enable|add)\s+(?:an?\s+)?(?:anchor|presenter|host|avatar|spokesperson|talking\s+head|narrator\s+character)|(?:anchor|presenter|host)\s*[:=]\s*(?:true|yes|1|on|always)/i.test(text);
|
|
287
|
+
}
|
|
288
|
+
|
|
282
289
|
// ── Component Theme Detection ──────────────────────────────────────────────────
|
|
283
290
|
/**
|
|
284
291
|
* Detect the graphic entity to render on the full canvas for a given scene.
|
|
285
292
|
*/
|
|
286
|
-
function detectComponentTheme({
|
|
293
|
+
function detectComponentTheme({
|
|
294
|
+
visualDescription = '',
|
|
295
|
+
visual_description = '',
|
|
296
|
+
prompt = '',
|
|
297
|
+
caption = '',
|
|
298
|
+
narration = '',
|
|
299
|
+
topic = '',
|
|
300
|
+
imagePath = null,
|
|
301
|
+
aesthetic = 'cyber_tech',
|
|
302
|
+
anchorWanted = true,
|
|
303
|
+
} = {}) {
|
|
287
304
|
if (imagePath) return 'image';
|
|
288
305
|
const visDesc = visualDescription || visual_description || prompt || '';
|
|
289
306
|
const text = `${visDesc} ${caption} ${narration} ${topic}`.toLowerCase();
|
|
290
307
|
|
|
308
|
+
// If anchor is NOT requested, suppress all humanoid doodle anchor characters
|
|
309
|
+
// and route to non-anchor procedural visual archetypes
|
|
310
|
+
if (!anchorWanted) {
|
|
311
|
+
// 1. Versus / Comparison / Split Screen
|
|
312
|
+
if (/(?:^|[^\w])(versus|vs|comparison|compare|pros and cons|pros vs cons|difference between|dichotomy|contrast|side-by-side|before and after|before vs after|split-screen|split screen|modern vs ancient|modern vs prehistoric|then and now|evolution of human)(?:[^\w]|$)/i.test(text)
|
|
313
|
+
|| /വ്യത്യാസം|താരതമ്യം|എതിര്|പോരാട്ടം|തമ്മിൽ|തുലനം|താരതമ്യപ്പെടുത്തൽ/.test(text)) {
|
|
314
|
+
return 'versus_comparison';
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 2. Magnification / Microscope / Fiber / Macro Inspection / Lice
|
|
318
|
+
if (/(?:^|[^\w])(magnify|magnification|zoom|microscopic|microscope|macro|close up|closeup|inspection|loupe|examine|scrutiny|detail|focal point|focus in|specimen|fiber|weave|parasite|lice|louse)(?:[^\w]|$)/i.test(text)
|
|
319
|
+
|| /സൂക്ഷ്മ|പരിശോധന|വലുതാക്കി കാണുക|സൂക്ഷ്മമായി|നിരീക്ഷണം|മൈക്രോസ്കോപ്പ്|പേൻ/.test(text)) {
|
|
320
|
+
return 'magnification_focus';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 3. Cognitive Insight / Psychology / Brain / Eureka / Thought / Future Planning / Shame Reflection
|
|
324
|
+
if (/(?:^|[^\w])(mind|psychology|cognitive|brain|insight|revelation|eureka|intuition|subconscious|thinking|thought|contemplation|intelligence|mental|awareness|neuroscience|concept|idea|epiphany|planning for tomorrow|future planning|foresight|shame|blush|embarrass|mirror|reflection|theory of mind|self-aware|identity)(?:[^\w]|$)/i.test(text)
|
|
325
|
+
|| /മനസ്സ്|ബുദ്ധി|ചിന്ത|മനഃശാസ്ത്രം|ജ്ഞാനം|മസ്തിഷ്കം|ബോധം|കണ്ണാടി|പ്രതിഫലനം|നാണം/.test(text)) {
|
|
326
|
+
return 'cognitive_insight';
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// 4. Mechanism / Physics / Machinery / Engine / Needle / Stitching / Cold
|
|
330
|
+
if (/(?:^|[^\w])(engine|gear|gears|motor|physics|aerodynamics|thrust|turbine|propulsion|piston|hydraulic|kinetic|machine|machinery|circuit|mechanics|thermodynamics|levers|pulley|needle|bone needle|sew|sewing|stitch|stitching|shiver|shivering|cold|freeze|freezing|ice|winter|protection flaw)(?:[^\w]|$)/i.test(text)
|
|
331
|
+
|| /യന്ത്രം|എഞ്ചിൻ|ഭൗതികശാസ്ത്രം|പ്രവർത്തനം|ചലനം|മെക്കാനിസം|ഭൗതികം|സൂചി|തുന്ന|വിറയ്ക്കുക|തണുപ്പ്/.test(text)) {
|
|
332
|
+
return 'mechanism_physics';
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 5. Process / Pipeline / Stages / Assembly / Tribe / Community / Hierarchy / Royal Status
|
|
336
|
+
if (/(?:^|[^\w])(pipeline|workflow|step by step|steps|stages|phase|phases|production line|assembly|manufacturing|lifecycle|funnel|conveyor|sequence|how it works|how it is made|procedure|tribe|tribal|clan|stranger|meeting strangers|social language|status in society|hierarchy|royal robe|royal robes|crown|sceptre|ornaments|status and power|wealth authority)(?:[^\w]|$)/i.test(text)
|
|
337
|
+
|| /ഘട്ടങ്ങൾ|പ്രക്രിയ|ഘട്ടം|ഉൽപ്പാദനം|വഴികൾ|പ്രവർത്തനരീതി|കൂട്ടമായി|സമൂഹം|ഗോത്രം|സാമൂഹിക സ്ഥാനം/.test(text)) {
|
|
338
|
+
return 'process_pipeline';
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 6. Radar Globe / World Map / Migration
|
|
342
|
+
if (/(?:^|[^\w])(globe|map|world|geography|radar|international|continent|territory|country|border|location|migration|migrating|migrate|africa highlighted|colder regions|out of africa)(?:[^\w]|$)/i.test(text)
|
|
343
|
+
|| /ഭൂപടം|ലോകം|ഭൂഖണ്ഡം|ഭൂപട|നക്ശാ|ക്ലോബ്|കുടിയേറ്റം|മൈഗ്രേഷൻ/.test(text)) {
|
|
344
|
+
return 'radar_globe';
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// 7. Celestial / Orbit
|
|
348
|
+
if (/(?:^|[^\w])(orbit|satellite|satellites|gps|planet|earth|mars|moon|galaxy|universe|solar|astronomy|gravity|space|telescope|interstellar)(?:[^\w]|$)/i.test(text)
|
|
349
|
+
|| /ബഹിരാകാശ|ഗ്രഹം|ഉപഗ്രഹ|ഭൂമി|നക്ഷത്ര|പ്രപഞ്ച|अंतरिक्ष|ग्रह|तारा|വിൺവെളി/.test(text)) {
|
|
350
|
+
return 'celestial_orbit';
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// 8. DNA / Biology
|
|
354
|
+
if (/(?:^|[^\w])(dna|helix|gene|cell|molecule|biology|organism|protein|virus|bacteria|medical|genetic|chromosome)(?:[^\w]|$)/i.test(text)
|
|
355
|
+
|| /ഡിഎൻഎ|കോശം|തന്മാത്ര|ജീവശാസ്ത്ര|डीएनए|कोशिका/.test(text)) {
|
|
356
|
+
return 'dna_helix';
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// 9. Code / Programming
|
|
360
|
+
if (/(?:^|[^\w])(code|function|syntax|api|javascript|python|typescript|json|sql|query|endpoint|algorithm|struct|cli|terminal|bash|shell|npm|git|compiler|linux|rust)(?:[^\w]|$)/.test(text)
|
|
361
|
+
|| /കോഡിംഗ്|സോഫ്റ്റ്വെയർ|പ്രോഗ്രാമിംഗ്|कोड|सॉफ्टवेयर/.test(text)) {
|
|
362
|
+
return 'code';
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 10. Chart / Finance
|
|
366
|
+
if (/(?:^|[^\w])(chart|data|analytics|growth|graph|statistic|bars|volume|distribution|stock|crypto|bitcoin|market|invest|trading|inflation|economy|gdp|finance|gain|loss|bull|bear|candlestick|price)(?:[^\w]|$)/.test(text)
|
|
367
|
+
|| /നിക്ഷേപ|ഓഹരി|വിപണി|പണം|ചാർട്ട്|शेयर|बाज़ार/.test(text)) {
|
|
368
|
+
return 'chart';
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// 11. Nodes / Neural Net
|
|
372
|
+
if (/(?:^|[^\w])(network|nodes|neural network|neural net|distributed system|server mesh|deep learning)(?:[^\w]|$)/.test(text)
|
|
373
|
+
|| /നെറ്റ്വർക്ക്|ശൃംഖല|नेटवर्क/.test(text)) {
|
|
374
|
+
return 'nodes';
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// 12. 3D Isometric Geometry
|
|
378
|
+
if (/(?:^|[^\w])(cube|geometry|3d|wireframe|isometric|matrix|crystal|structure|polyhedron|dimension|architect|blueprint|math)(?:[^\w]|$)/.test(text)
|
|
379
|
+
|| /ജ്യാമിതി|രൂപം|ത്രിമാന|संरचना/.test(text)) {
|
|
380
|
+
return 'isometric_geometry';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// 13. Speed Action / Hunter
|
|
384
|
+
if (/(?:^|[^\w])(speed|sprint|velocity|fast|run|race|motion|hunter|hunting|spear|savannah|blazing sun|mosquito)(?:[^\w]|$)/.test(text)
|
|
385
|
+
|| /വേഗത|ഓട്ടം|കുന്തം|വേഗം/.test(text)) {
|
|
386
|
+
return 'speed_action';
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// 14. Quote / Metric
|
|
390
|
+
if (/(?:^|[^\w])(quote|saying|proverb|statement|speech|wisdom)(?:[^\w]|$)/.test(text)) return 'quote';
|
|
391
|
+
if (/(?:^|[^\w])(metric|kpi|target|counter|percentage|ratio|score)(?:[^\w]|$)/.test(text)) return 'metric';
|
|
392
|
+
|
|
393
|
+
// 15. Timeline (strictly explicit roadmap/milestone chart requests only)
|
|
394
|
+
if (/(?:^|[^\w])(timeline|roadmap|milestone chart|milestone roadmap)(?:[^\w]|$)/i.test(text)
|
|
395
|
+
|| /ടൈംലൈൻ|റോഡ്മാപ്പ്/.test(text)) {
|
|
396
|
+
return 'timeline';
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// 16. Explainer Presentation (General visual lecture, whiteboard, tutorial, clothing topic, subscribe)
|
|
400
|
+
if (/(?:^|[^\w])(explain|explainer|presentation|lecture|teach|tutorial|whiteboard|blackboard|breakdown|guide|overview|theory|principles|fundamentals|introduction|summary|lesson|masterclass|subscribe|notification bell|outro|dress|dressing|wearing clothes|shirt|wardrobe|closet|hanger|outfit|wedding dress)(?:[^\w]|$)/i.test(text)
|
|
401
|
+
|| /വിവരണം|പഠിപ്പിക്കുക|ക്ലാസ്|പാഠം|തത്വം|വിവരിക്കുക|സബ്സ്ക്രൈബ്|വസ്ത്രധാരണം/.test(text)) {
|
|
402
|
+
return 'explainer_presentation';
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// 17. Waveform
|
|
406
|
+
if (/(?:^|[^\w])(waveform|audio|sound|equalizer|frequency|acoustics|hz|decibel|music spectrum|voice waveform)(?:[^\w]|$)/i.test(text)
|
|
407
|
+
|| /ശബ്ദതരംഗം|തരംഗം|സംഗീതം|ധ്വനി/.test(text)) {
|
|
408
|
+
return 'waveform';
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Clean aesthetic fallbacks — strictly non-anchor
|
|
412
|
+
if (aesthetic === 'cosmic_space') return 'celestial_orbit';
|
|
413
|
+
if (aesthetic === 'cinematic_documentary') return 'explainer_presentation';
|
|
414
|
+
if (aesthetic === 'finance_executive') return 'chart';
|
|
415
|
+
if (aesthetic === 'nature_organic') return 'dna_helix';
|
|
416
|
+
if (aesthetic === 'breaking_news') return 'radar_globe';
|
|
417
|
+
if (aesthetic === 'cyber_tech') return 'nodes';
|
|
418
|
+
if (aesthetic === 'minimal_luxury') return 'explainer_presentation';
|
|
419
|
+
if (aesthetic === 'hype_sports') return 'speed_action';
|
|
420
|
+
return 'explainer_presentation';
|
|
421
|
+
}
|
|
422
|
+
|
|
291
423
|
// ── Doodled Character Actions (Human Stories, History, Evolution, Daily Life) ──
|
|
292
424
|
// 1. Microscope / Lice Theory / Parasite / Genetics (Scene 7)
|
|
293
425
|
if (/(?:^|[^\w])(lice|louse|microscope|fiber|weave|parasite|lice evolution|genetic biology proof)(?:[^\w]|$)/i.test(text)
|
|
@@ -326,7 +458,7 @@ function detectComponentTheme({ visualDescription = '', visual_description = '',
|
|
|
326
458
|
}
|
|
327
459
|
|
|
328
460
|
// 7. Shame / Mirror / Reflection / Water / Self Awareness / Theory of Mind (Scene 3, 9)
|
|
329
|
-
if (/(?:^|[^\w])(shame|blush|embarrass|mirror|reflection|water pool|theory of mind|how others see|self-aware|identity|looking at me|psychology|mind-shift)(?:[^\w]|$)/i.test(text)
|
|
461
|
+
if (/(?:^|[^\w])(shame|blush|embarrass|mirror|reflection|water pool|theory of mind|how others see|self-aware|identity|looking at me|shame psychology|mind-shift)(?:[^\w]|$)/i.test(text)
|
|
330
462
|
|| /കണ്ണാടി|പ്രതിഫലനം|മറ്റുള്ളവർ എന്നെ|തിയറി ഓഫ് മൈൻഡ്|സ്വന്തം ശരീരം മറച്ചു|വ്യക്തിത്വം|ശ്രദ്ധിക്കുന്നുണ്ട്|യഥാർത്ഥ രഹസ്യം|നാണം മറയ്ക്കാൻ/.test(text)) {
|
|
331
463
|
return 'doodle_shame_reflection';
|
|
332
464
|
}
|
|
@@ -392,7 +524,7 @@ function detectComponentTheme({ visualDescription = '', visual_description = '',
|
|
|
392
524
|
}
|
|
393
525
|
|
|
394
526
|
// High Velocity Speed Action
|
|
395
|
-
if (/(?:^|[^\w])(sports|workout|gym|athlete|speed|streaks|race|championship|extreme|
|
|
527
|
+
if (/(?:^|[^\w])(sports|workout|gym|athlete|speed|streaks|race|championship|extreme|hype|adrenaline|velocity|sprint|fast motion)(?:[^\w]|$)/.test(text)
|
|
396
528
|
|| /കായിക|മത്സര|വേഗത|വ്യായാമം|खेल|दौड़/.test(text)) {
|
|
397
529
|
return 'speed_action';
|
|
398
530
|
}
|
|
@@ -421,9 +553,9 @@ function detectComponentTheme({ visualDescription = '', visual_description = '',
|
|
|
421
553
|
return 'chart';
|
|
422
554
|
}
|
|
423
555
|
|
|
424
|
-
// Neural Network Nodes
|
|
425
|
-
if (/(?:^|[^\w])(network|
|
|
426
|
-
||
|
|
556
|
+
// Neural Network Nodes / AI Topology
|
|
557
|
+
if (/(?:^|[^\w])(network|nodes|neural network|neural net|distributed system|server mesh|deep learning)(?:[^\w]|$)/.test(text)
|
|
558
|
+
|| /നെറ്റ്വർക്ക്|ശൃംഖല|नेटवर्क/.test(text)) {
|
|
427
559
|
return 'nodes';
|
|
428
560
|
}
|
|
429
561
|
|
|
@@ -433,16 +565,59 @@ function detectComponentTheme({ visualDescription = '', visual_description = '',
|
|
|
433
565
|
return 'isometric_geometry';
|
|
434
566
|
}
|
|
435
567
|
|
|
436
|
-
//
|
|
568
|
+
// ── Universal Semantic Visual Archetypes (General Content & Education) ──
|
|
569
|
+
// 1. Versus / Comparison / Split Screen
|
|
570
|
+
if (/(?:^|[^\w])(versus|vs|comparison|compare|pros and cons|pros vs cons|difference between|dichotomy|contrast|side-by-side|before and after|before vs after)(?:[^\w]|$)/i.test(text)
|
|
571
|
+
|| /വ്യത്യാസം|താരതമ്യം|എതിര്|പോരാട്ടം|തമ്മിൽ|തുലനം|താരതമ്യപ്പെടുത്തൽ/.test(text)) {
|
|
572
|
+
return 'versus_comparison';
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// 2. Mechanism / Physics / Machinery / Engine / Aerodynamics
|
|
576
|
+
if (/(?:^|[^\w])(engine|gear|gears|motor|physics|aerodynamics|thrust|turbine|propulsion|piston|hydraulic|kinetic|machine|machinery|circuit|mechanics|thermodynamics|levers|pulley|internal combustion|mechanical|cutaway|airflow|lift|rotor|dynamo)(?:[^\w]|$)/i.test(text)
|
|
577
|
+
|| /യന്ത്രം|എഞ്ചിൻ|ഭൗതികശാസ്ത്രം|പ്രവർത്തനം|ചലനം|മെക്കാനിസം|ഭൗതികം/.test(text)) {
|
|
578
|
+
return 'mechanism_physics';
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// 3. Process / Pipeline / Workflow / Stages / Assembly / Lifecycle
|
|
582
|
+
if (/(?:^|[^\w])(pipeline|workflow|step by step|steps|stages|phase|phases|production line|assembly|manufacturing|lifecycle|funnel|conveyor|sequence|how it works|how it is made|procedure)(?:[^\w]|$)/i.test(text)
|
|
583
|
+
|| /ഘട്ടങ്ങൾ|പ്രക്രിയ|ഘട്ടം|ഉൽപ്പാദനം|വഴികൾ|പ്രവർത്തനരീതി/.test(text)) {
|
|
584
|
+
return 'process_pipeline';
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// 4. Cognitive Insight / Psychology / Brain / Eureka / Revelation / Mind
|
|
588
|
+
if (/(?:^|[^\w])(mind|psychology|cognitive|brain|insight|revelation|eureka|intuition|subconscious|thinking|thought|contemplation|intelligence|mental|awareness|neuroscience|concept|idea|epiphany)(?:[^\w]|$)/i.test(text)
|
|
589
|
+
|| /മനസ്സ്|ബുദ്ധി|ചിന്ത|മനഃശാസ്ത്രം|ജ്ഞാനം|മസ്തിഷ്കം|ബോധം/.test(text)) {
|
|
590
|
+
return 'cognitive_insight';
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// 5. Magnification / Loupe / Macro Zoom / Scrutiny / Detailed Inspection
|
|
594
|
+
if (/(?:^|[^\w])(magnify|magnification|zoom|microscopic|macro|close up|closeup|inspection|loupe|examine|scrutiny|detail|focal point|focus in|specimen)(?:[^\w]|$)/i.test(text)
|
|
595
|
+
|| /സൂക്ഷ്മ|പരിശോധന|വലുതാക്കി കാണുക|സൂക്ഷ്മമായി|നിരീക്ഷണം/.test(text)) {
|
|
596
|
+
return 'magnification_focus';
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// 6. Explainer / Presentation / Tutorial / Masterclass Whiteboard / Breakdown
|
|
600
|
+
if (/(?:^|[^\w])(explain|explainer|presentation|lecture|teach|tutorial|whiteboard|blackboard|breakdown|guide|overview|theory|principles|fundamentals|introduction|summary|lesson|masterclass)(?:[^\w]|$)/i.test(text)
|
|
601
|
+
|| /വിവരണം|പഠിപ്പിക്കുക|ക്ലാസ്|പാഠം|തത്വം|വിവരിക്കുക/.test(text)) {
|
|
602
|
+
return 'explainer_presentation';
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// 7. Waveform / Audio / Sound / Acoustics / Equalizer
|
|
606
|
+
if (/(?:^|[^\w])(waveform|audio|sound|equalizer|frequency|acoustics|hz|decibel|music spectrum|voice waveform)(?:[^\w]|$)/i.test(text)
|
|
607
|
+
|| /ശബ്ദതരംഗം|തരംഗം|സംഗീതം|ധ്വനി/.test(text)) {
|
|
608
|
+
return 'waveform';
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Fallbacks matched to aesthetic (rich semantic defaults, never sterile)
|
|
437
612
|
if (aesthetic === 'cosmic_space') return 'celestial_orbit';
|
|
438
613
|
if (aesthetic === 'cinematic_documentary') return 'doodle_general_human';
|
|
439
614
|
if (aesthetic === 'finance_executive') return 'chart';
|
|
440
615
|
if (aesthetic === 'nature_organic') return 'dna_helix';
|
|
441
616
|
if (aesthetic === 'breaking_news') return 'radar_globe';
|
|
442
617
|
if (aesthetic === 'cyber_tech') return 'nodes';
|
|
443
|
-
if (aesthetic === 'minimal_luxury') return '
|
|
618
|
+
if (aesthetic === 'minimal_luxury') return 'explainer_presentation';
|
|
444
619
|
if (aesthetic === 'hype_sports') return 'speed_action';
|
|
445
|
-
return '
|
|
620
|
+
return 'explainer_presentation';
|
|
446
621
|
}
|
|
447
622
|
|
|
448
623
|
// ── Harness HTML Wrapper for User / Agent Code ────────────────────────────────
|
|
@@ -573,12 +748,18 @@ function generateSceneHtml({
|
|
|
573
748
|
height = 1080,
|
|
574
749
|
sceneIndex = 0,
|
|
575
750
|
showCaptions = false,
|
|
751
|
+
showAnchor = false,
|
|
576
752
|
} = {}) {
|
|
577
753
|
const captionsWanted = Boolean(
|
|
578
754
|
showCaptions ||
|
|
579
755
|
detectCaptionsRequested(prompt || topic) ||
|
|
580
756
|
process.env.FRAMEFORGE_SHOW_CAPTIONS === '1'
|
|
581
757
|
);
|
|
758
|
+
const anchorWanted = Boolean(
|
|
759
|
+
showAnchor ||
|
|
760
|
+
detectAnchorRequested(prompt || topic) ||
|
|
761
|
+
process.env.FRAMEFORGE_SHOW_ANCHOR === '1'
|
|
762
|
+
);
|
|
582
763
|
const aestheticKey = detectAesthetic({
|
|
583
764
|
topic,
|
|
584
765
|
style,
|
|
@@ -599,6 +780,7 @@ function generateSceneHtml({
|
|
|
599
780
|
topic,
|
|
600
781
|
imagePath,
|
|
601
782
|
aesthetic: aestheticKey,
|
|
783
|
+
anchorWanted,
|
|
602
784
|
});
|
|
603
785
|
const clampedDur = Math.max(1, Number(duration) || 3);
|
|
604
786
|
|
|
@@ -691,6 +873,7 @@ function spring(t, delay = 0, tension = ${aesthetic.springTension}, friction = $
|
|
|
691
873
|
}
|
|
692
874
|
|
|
693
875
|
const SHOW_CAPTIONS = ${JSON.stringify(captionsWanted)};
|
|
876
|
+
const SHOW_ANCHOR = ${JSON.stringify(anchorWanted)};
|
|
694
877
|
const AESTHETIC = ${JSON.stringify(aestheticKey)};
|
|
695
878
|
const BG_TYPE = ${JSON.stringify(aesthetic.backgroundType)};
|
|
696
879
|
const COMP_THEME = ${JSON.stringify(compTheme)};
|
|
@@ -712,6 +895,8 @@ const IS_LIGHT = ${Boolean(pal.isLight)};
|
|
|
712
895
|
const STROKE_COLOR = ${JSON.stringify(pal.isLight ? (pal.stroke || '#1c1917') : '#f8fafc')};
|
|
713
896
|
const STROKE_ALT = ${JSON.stringify(pal.isLight ? (pal.strokeAlt || '#44403c') : 'rgba(255, 255, 255, 0.7)')};
|
|
714
897
|
const AMBIENT_LINE = ${JSON.stringify(pal.isLight ? 'rgba(28, 25, 23, 0.22)' : 'rgba(255, 255, 255, 0.25)')};
|
|
898
|
+
const TEXT_MAIN = ${JSON.stringify(pal.isLight ? '#1c1917' : '#f8fafc')};
|
|
899
|
+
const TEXT_MUTED = ${JSON.stringify(pal.isLight ? '#78716c' : '#94a3b8')};
|
|
715
900
|
|
|
716
901
|
let cachedImage = null;
|
|
717
902
|
if (IMAGE_SRC) {
|
|
@@ -961,6 +1146,7 @@ function drawMainScene(t) {
|
|
|
961
1146
|
showSpecular = false,
|
|
962
1147
|
seed = 100,
|
|
963
1148
|
} = {}) {
|
|
1149
|
+
if (!SHOW_ANCHOR) return;
|
|
964
1150
|
ctx.save();
|
|
965
1151
|
ctx.translate(x, y);
|
|
966
1152
|
ctx.scale(scale, scale);
|
|
@@ -3313,7 +3499,679 @@ function drawMainScene(t) {
|
|
|
3313
3499
|
ctx.drawImage(cachedImage, cx - imgW / 2, cy - imgH / 2, imgW, imgH);
|
|
3314
3500
|
ctx.restore();
|
|
3315
3501
|
|
|
3316
|
-
} else {
|
|
3502
|
+
} else if (COMP_THEME === 'versus_comparison') {
|
|
3503
|
+
// Universal Archetype: Split-Screen Versus / Comparison
|
|
3504
|
+
const splitSpring = spring(t, 0.05, 180, 14);
|
|
3505
|
+
const cardLeftSpring = spring(t, 0.15, 190, 15);
|
|
3506
|
+
const cardRightSpring = spring(t, 0.28, 190, 15);
|
|
3507
|
+
const badgeSpring = spring(t, 0.42, 220, 13);
|
|
3508
|
+
|
|
3509
|
+
// Center divider beam
|
|
3510
|
+
const divAlpha = Math.min(1, Math.max(0, splitSpring));
|
|
3511
|
+
ctx.save();
|
|
3512
|
+
ctx.globalAlpha = divAlpha;
|
|
3513
|
+
const seamGrad = ctx.createLinearGradient(0, cy - H * 0.42, 0, cy + H * 0.42);
|
|
3514
|
+
seamGrad.addColorStop(0, 'rgba(255,255,255,0)');
|
|
3515
|
+
seamGrad.addColorStop(0.5, ACCENT);
|
|
3516
|
+
seamGrad.addColorStop(1, 'rgba(255,255,255,0)');
|
|
3517
|
+
ctx.strokeStyle = seamGrad;
|
|
3518
|
+
ctx.lineWidth = 3;
|
|
3519
|
+
ctx.beginPath();
|
|
3520
|
+
ctx.moveTo(cx, cy - H * 0.42);
|
|
3521
|
+
ctx.lineTo(cx, cy + H * 0.42);
|
|
3522
|
+
ctx.stroke();
|
|
3523
|
+
|
|
3524
|
+
// Seam energy sparks
|
|
3525
|
+
for (let sp = 0; sp < 8; sp++) {
|
|
3526
|
+
const spY = cy - H * 0.35 + ((sp * 95 + t * 240) % (H * 0.7));
|
|
3527
|
+
const spR = 3 + Math.sin(t * 8 + sp) * 2;
|
|
3528
|
+
ctx.fillStyle = ACCENT;
|
|
3529
|
+
ctx.beginPath();
|
|
3530
|
+
ctx.arc(cx, spY, spR, 0, Math.PI * 2);
|
|
3531
|
+
ctx.fill();
|
|
3532
|
+
}
|
|
3533
|
+
ctx.restore();
|
|
3534
|
+
|
|
3535
|
+
// Left Card (Side A)
|
|
3536
|
+
const cardW = Math.min(W * 0.38, 520);
|
|
3537
|
+
const cardH = Math.min(H * 0.62, 540);
|
|
3538
|
+
const leftX = cx - cardW - 35;
|
|
3539
|
+
const cardY = cy - cardH / 2;
|
|
3540
|
+
|
|
3541
|
+
ctx.save();
|
|
3542
|
+
ctx.globalAlpha = Math.min(1, Math.max(0, cardLeftSpring));
|
|
3543
|
+
ctx.translate(leftX + (1 - cardLeftSpring) * -60, cardY);
|
|
3544
|
+
ctx.fillStyle = CARD_BG;
|
|
3545
|
+
ctx.strokeStyle = CARD_BORDER;
|
|
3546
|
+
ctx.lineWidth = 2;
|
|
3547
|
+
ctx.beginPath();
|
|
3548
|
+
ctx.roundRect(0, 0, cardW, cardH, 20);
|
|
3549
|
+
ctx.fill();
|
|
3550
|
+
ctx.stroke();
|
|
3551
|
+
|
|
3552
|
+
// Left Card Header & Accent Pill
|
|
3553
|
+
ctx.fillStyle = 'rgba(56, 189, 248, 0.15)';
|
|
3554
|
+
ctx.beginPath();
|
|
3555
|
+
ctx.roundRect(24, 24, 110, 32, 16);
|
|
3556
|
+
ctx.fill();
|
|
3557
|
+
ctx.fillStyle = '#38bdf8';
|
|
3558
|
+
ctx.font = 'bold 16px ' + FONT_DISPLAY;
|
|
3559
|
+
ctx.fillText('OPTION A', 42, 45);
|
|
3560
|
+
|
|
3561
|
+
// Left Metrics / Feature Bars
|
|
3562
|
+
for (let b = 0; b < 4; b++) {
|
|
3563
|
+
const barY = 90 + b * 75;
|
|
3564
|
+
const bProgress = Math.min(1, Math.max(0, spring(t, 0.3 + b * 0.1, 160, 14)));
|
|
3565
|
+
ctx.fillStyle = IS_LIGHT ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.06)';
|
|
3566
|
+
ctx.beginPath();
|
|
3567
|
+
ctx.roundRect(24, barY + 22, cardW - 48, 14, 7);
|
|
3568
|
+
ctx.fill();
|
|
3569
|
+
|
|
3570
|
+
ctx.fillStyle = '#38bdf8';
|
|
3571
|
+
ctx.beginPath();
|
|
3572
|
+
const fillW = (cardW - 48) * (0.45 + 0.15 * Math.sin(b * 1.5 + 2)) * bProgress;
|
|
3573
|
+
ctx.roundRect(24, barY + 22, fillW, 14, 7);
|
|
3574
|
+
ctx.fill();
|
|
3575
|
+
|
|
3576
|
+
// Feature icon / indicator
|
|
3577
|
+
sCircle(36, barY + 8, 6, 100 + b, '#38bdf8', 2, '#38bdf8');
|
|
3578
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
3579
|
+
ctx.font = '600 18px ' + FONT_DISPLAY;
|
|
3580
|
+
ctx.fillText('ATTRIBUTE 0' + (b + 1), 54, barY + 14);
|
|
3581
|
+
}
|
|
3582
|
+
ctx.restore();
|
|
3583
|
+
|
|
3584
|
+
// Right Card (Side B)
|
|
3585
|
+
const rightX = cx + 35;
|
|
3586
|
+
ctx.save();
|
|
3587
|
+
ctx.globalAlpha = Math.min(1, Math.max(0, cardRightSpring));
|
|
3588
|
+
ctx.translate(rightX + (1 - cardRightSpring) * 60, cardY);
|
|
3589
|
+
ctx.fillStyle = CARD_BG;
|
|
3590
|
+
ctx.strokeStyle = CARD_BORDER;
|
|
3591
|
+
ctx.lineWidth = 2;
|
|
3592
|
+
ctx.beginPath();
|
|
3593
|
+
ctx.roundRect(0, 0, cardW, cardH, 20);
|
|
3594
|
+
ctx.fill();
|
|
3595
|
+
ctx.stroke();
|
|
3596
|
+
|
|
3597
|
+
// Right Card Header & Accent Pill
|
|
3598
|
+
ctx.fillStyle = 'rgba(244, 63, 94, 0.15)';
|
|
3599
|
+
ctx.beginPath();
|
|
3600
|
+
ctx.roundRect(24, 24, 110, 32, 16);
|
|
3601
|
+
ctx.fill();
|
|
3602
|
+
ctx.fillStyle = '#f43f5e';
|
|
3603
|
+
ctx.font = 'bold 16px ' + FONT_DISPLAY;
|
|
3604
|
+
ctx.fillText('OPTION B', 42, 45);
|
|
3605
|
+
|
|
3606
|
+
// Right Metrics / Feature Bars
|
|
3607
|
+
for (let b = 0; b < 4; b++) {
|
|
3608
|
+
const barY = 90 + b * 75;
|
|
3609
|
+
const bProgress = Math.min(1, Math.max(0, spring(t, 0.35 + b * 0.1, 160, 14)));
|
|
3610
|
+
ctx.fillStyle = IS_LIGHT ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.06)';
|
|
3611
|
+
ctx.beginPath();
|
|
3612
|
+
ctx.roundRect(24, barY + 22, cardW - 48, 14, 7);
|
|
3613
|
+
ctx.fill();
|
|
3614
|
+
|
|
3615
|
+
ctx.fillStyle = '#f43f5e';
|
|
3616
|
+
ctx.beginPath();
|
|
3617
|
+
const fillW = (cardW - 48) * (0.55 + 0.12 * Math.cos(b * 1.8 + 1)) * bProgress;
|
|
3618
|
+
ctx.roundRect(24, barY + 22, fillW, 14, 7);
|
|
3619
|
+
ctx.fill();
|
|
3620
|
+
|
|
3621
|
+
sCircle(36, barY + 8, 6, 200 + b, '#f43f5e', 2, '#f43f5e');
|
|
3622
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
3623
|
+
ctx.font = '600 18px ' + FONT_DISPLAY;
|
|
3624
|
+
ctx.fillText('ATTRIBUTE 0' + (b + 1), 54, barY + 14);
|
|
3625
|
+
}
|
|
3626
|
+
ctx.restore();
|
|
3627
|
+
|
|
3628
|
+
// Center VS Emblem Badge
|
|
3629
|
+
ctx.save();
|
|
3630
|
+
ctx.translate(cx, cy);
|
|
3631
|
+
const bScale = Math.max(0, badgeSpring);
|
|
3632
|
+
ctx.scale(bScale, bScale);
|
|
3633
|
+
ctx.fillStyle = IS_LIGHT ? '#ffffff' : '#0f172a';
|
|
3634
|
+
ctx.strokeStyle = ACCENT;
|
|
3635
|
+
ctx.lineWidth = 4;
|
|
3636
|
+
ctx.beginPath();
|
|
3637
|
+
ctx.arc(0, 0, 48, 0, Math.PI * 2);
|
|
3638
|
+
ctx.fill();
|
|
3639
|
+
ctx.stroke();
|
|
3640
|
+
|
|
3641
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
|
|
3642
|
+
ctx.lineWidth = 1.5;
|
|
3643
|
+
ctx.beginPath();
|
|
3644
|
+
ctx.arc(0, 0, 42, 0, Math.PI * 2);
|
|
3645
|
+
ctx.stroke();
|
|
3646
|
+
|
|
3647
|
+
ctx.font = '900 28px ' + FONT_DISPLAY;
|
|
3648
|
+
ctx.fillStyle = ACCENT;
|
|
3649
|
+
ctx.textAlign = 'center';
|
|
3650
|
+
ctx.textBaseline = 'middle';
|
|
3651
|
+
ctx.fillText('VS', 0, 2);
|
|
3652
|
+
ctx.restore();
|
|
3653
|
+
|
|
3654
|
+
} else if (COMP_THEME === 'mechanism_physics') {
|
|
3655
|
+
// Universal Archetype: Mechanical Engineering, Cutaway Gears, Forces & Kinematics
|
|
3656
|
+
const engSpring = spring(t, 0.05, 170, 14);
|
|
3657
|
+
const vectorSpring = spring(t, 0.45, 180, 15);
|
|
3658
|
+
|
|
3659
|
+
// Background schematic blueprint grid lines
|
|
3660
|
+
ctx.save();
|
|
3661
|
+
ctx.strokeStyle = IS_LIGHT ? 'rgba(0,0,0,0.07)' : 'rgba(56, 189, 248, 0.12)';
|
|
3662
|
+
ctx.lineWidth = 1;
|
|
3663
|
+
for (let gx = cx - 450; gx <= cx + 450; gx += 60) {
|
|
3664
|
+
ctx.beginPath(); ctx.moveTo(gx, cy - 320); ctx.lineTo(gx, cy + 320); ctx.stroke();
|
|
3665
|
+
}
|
|
3666
|
+
for (let gy = cy - 300; gy <= cy + 300; gy += 60) {
|
|
3667
|
+
ctx.beginPath(); ctx.moveTo(cx - 480, gy); ctx.lineTo(cx + 480, gy); ctx.stroke();
|
|
3668
|
+
}
|
|
3669
|
+
ctx.strokeStyle = IS_LIGHT ? 'rgba(0,0,0,0.1)' : 'rgba(56, 189, 248, 0.2)';
|
|
3670
|
+
ctx.lineWidth = 1.5;
|
|
3671
|
+
ctx.beginPath(); ctx.arc(cx, cy, 340, 0, Math.PI * 2); ctx.stroke();
|
|
3672
|
+
ctx.restore();
|
|
3673
|
+
|
|
3674
|
+
// Helper: Draw precision technical gear with teeth
|
|
3675
|
+
function drawGear(gx, gy, radius, teeth, rotAngle, color, axleColor) {
|
|
3676
|
+
ctx.save();
|
|
3677
|
+
ctx.translate(gx, gy);
|
|
3678
|
+
ctx.rotate(rotAngle);
|
|
3679
|
+
ctx.fillStyle = color;
|
|
3680
|
+
ctx.strokeStyle = STROKE_COLOR;
|
|
3681
|
+
ctx.lineWidth = 2.5;
|
|
3682
|
+
|
|
3683
|
+
ctx.beginPath();
|
|
3684
|
+
const toothDepth = radius * 0.14;
|
|
3685
|
+
for (let i = 0; i < teeth; i++) {
|
|
3686
|
+
const a0 = (i * 2 * Math.PI) / teeth;
|
|
3687
|
+
const a1 = a0 + (Math.PI / teeth) * 0.35;
|
|
3688
|
+
const a2 = a0 + (Math.PI / teeth) * 0.65;
|
|
3689
|
+
const a3 = a0 + (Math.PI / teeth);
|
|
3690
|
+
|
|
3691
|
+
const rIn = radius - toothDepth;
|
|
3692
|
+
const rOut = radius + toothDepth;
|
|
3693
|
+
|
|
3694
|
+
if (i === 0) ctx.moveTo(rIn * Math.cos(a0), rIn * Math.sin(a0));
|
|
3695
|
+
else ctx.lineTo(rIn * Math.cos(a0), rIn * Math.sin(a0));
|
|
3696
|
+
ctx.lineTo(rOut * Math.cos(a1), rOut * Math.sin(a1));
|
|
3697
|
+
ctx.lineTo(rOut * Math.cos(a2), rOut * Math.sin(a2));
|
|
3698
|
+
ctx.lineTo(rIn * Math.cos(a3), rIn * Math.sin(a3));
|
|
3699
|
+
}
|
|
3700
|
+
ctx.closePath();
|
|
3701
|
+
ctx.fill();
|
|
3702
|
+
ctx.stroke();
|
|
3703
|
+
|
|
3704
|
+
// Hollow interior & spokes
|
|
3705
|
+
ctx.beginPath();
|
|
3706
|
+
ctx.arc(0, 0, radius * 0.62, 0, Math.PI * 2);
|
|
3707
|
+
ctx.fillStyle = IS_LIGHT ? '#ffffff' : '#080d1a';
|
|
3708
|
+
ctx.fill();
|
|
3709
|
+
ctx.stroke();
|
|
3710
|
+
|
|
3711
|
+
// 4 Precision Spokes
|
|
3712
|
+
for (let sp = 0; sp < 4; sp++) {
|
|
3713
|
+
ctx.save();
|
|
3714
|
+
ctx.rotate((sp * Math.PI) / 2);
|
|
3715
|
+
ctx.fillStyle = color;
|
|
3716
|
+
ctx.fillRect(-6, -radius * 0.58, 12, radius * 0.58 * 2);
|
|
3717
|
+
ctx.restore();
|
|
3718
|
+
}
|
|
3719
|
+
|
|
3720
|
+
// Center Axle Hub
|
|
3721
|
+
ctx.beginPath();
|
|
3722
|
+
ctx.arc(0, 0, radius * 0.25, 0, Math.PI * 2);
|
|
3723
|
+
ctx.fillStyle = axleColor;
|
|
3724
|
+
ctx.fill();
|
|
3725
|
+
ctx.stroke();
|
|
3726
|
+
sCircle(0, 0, radius * 0.1, 50, '#ffffff', 2, '#ffffff');
|
|
3727
|
+
|
|
3728
|
+
ctx.restore();
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3731
|
+
ctx.save();
|
|
3732
|
+
const gScale = Math.min(1, Math.max(0, engSpring));
|
|
3733
|
+
ctx.translate(cx, cy);
|
|
3734
|
+
ctx.scale(gScale, gScale);
|
|
3735
|
+
ctx.translate(-cx, -cy);
|
|
3736
|
+
|
|
3737
|
+
// Primary Gear Train (Interlocking 3 gears)
|
|
3738
|
+
const gearSpeed = t * 1.4;
|
|
3739
|
+
drawGear(cx - 150, cy, 140, 24, gearSpeed, IS_LIGHT ? '#e2e8f0' : '#1e293b', ACCENT);
|
|
3740
|
+
drawGear(cx + 90, cy - 80, 105, 18, -gearSpeed * (24 / 18) + 0.17, IS_LIGHT ? '#cbd5e1' : '#334155', ACCENT2);
|
|
3741
|
+
drawGear(cx + 175, cy + 95, 70, 12, gearSpeed * (24 / 12) + 0.25, IS_LIGHT ? '#94a3b8' : '#475569', '#10b981');
|
|
3742
|
+
|
|
3743
|
+
// Dynamic Kinematic Linkage / Piston
|
|
3744
|
+
const pistonCycle = t * 3.5;
|
|
3745
|
+
const crankR = 60;
|
|
3746
|
+
const crankX = cx - 150 + Math.cos(gearSpeed) * crankR;
|
|
3747
|
+
const crankY = cy + Math.sin(gearSpeed) * crankR;
|
|
3748
|
+
const pistonY = cy + 180 + Math.sin(pistonCycle) * 35;
|
|
3749
|
+
const pistonX = cx - 150;
|
|
3750
|
+
|
|
3751
|
+
sLine(crankX, crankY, pistonX, pistonY, 301, ACCENT, 5);
|
|
3752
|
+
sCircle(crankX, crankY, 8, 302, '#ffffff', 2, ACCENT);
|
|
3753
|
+
sCircle(pistonX, pistonY, 8, 303, '#ffffff', 2, ACCENT);
|
|
3754
|
+
|
|
3755
|
+
// Piston Head in Cylinder
|
|
3756
|
+
ctx.fillStyle = IS_LIGHT ? '#64748b' : '#38bdf8';
|
|
3757
|
+
ctx.fillRect(pistonX - 35, pistonY - 15, 70, 30);
|
|
3758
|
+
ctx.strokeStyle = STROKE_COLOR;
|
|
3759
|
+
ctx.lineWidth = 2.5;
|
|
3760
|
+
ctx.strokeRect(pistonX - 35, pistonY - 15, 70, 30);
|
|
3761
|
+
|
|
3762
|
+
// Dynamic Force Vectors & Aerodynamic Flow Arrows
|
|
3763
|
+
if (vectorSpring > 0.05) {
|
|
3764
|
+
ctx.save();
|
|
3765
|
+
ctx.globalAlpha = Math.min(1, Math.max(0, vectorSpring));
|
|
3766
|
+
|
|
3767
|
+
const vX = cx + 290;
|
|
3768
|
+
const vY = cy - 140;
|
|
3769
|
+
sLine(vX, vY, vX + 110, vY, 401, '#10b981', 4);
|
|
3770
|
+
ctx.fillStyle = '#10b981';
|
|
3771
|
+
ctx.beginPath();
|
|
3772
|
+
ctx.moveTo(vX + 125, vY);
|
|
3773
|
+
ctx.lineTo(vX + 105, vY - 8);
|
|
3774
|
+
ctx.lineTo(vX + 105, vY + 8);
|
|
3775
|
+
ctx.closePath();
|
|
3776
|
+
ctx.fill();
|
|
3777
|
+
ctx.font = 'bold 15px ' + FONT_DISPLAY;
|
|
3778
|
+
ctx.fillText('F_THRUST = 4.8 kN', vX, vY - 16);
|
|
3779
|
+
|
|
3780
|
+
ctx.strokeStyle = '#f59e0b';
|
|
3781
|
+
ctx.lineWidth = 3;
|
|
3782
|
+
ctx.beginPath();
|
|
3783
|
+
ctx.arc(cx - 150, cy, 185, -0.6, 0.6);
|
|
3784
|
+
ctx.stroke();
|
|
3785
|
+
ctx.fillStyle = '#f59e0b';
|
|
3786
|
+
ctx.font = 'bold 15px ' + FONT_DISPLAY;
|
|
3787
|
+
ctx.fillText('TORQUE = 320 Nm', cx - 210, cy - 195);
|
|
3788
|
+
ctx.restore();
|
|
3789
|
+
}
|
|
3790
|
+
|
|
3791
|
+
ctx.restore();
|
|
3792
|
+
|
|
3793
|
+
} else if (COMP_THEME === 'process_pipeline') {
|
|
3794
|
+
// Universal Archetype: Multi-Stage Process / Pipeline / Supply Chain / Lifecycle
|
|
3795
|
+
const pipeSpring = spring(t, 0.05, 170, 14);
|
|
3796
|
+
const stages = [
|
|
3797
|
+
{ name: 'INGEST', label: '01 / SOURCE', desc: 'Raw Signals' },
|
|
3798
|
+
{ name: 'SYNTHESIS', label: '02 / TRANSFORM', desc: 'Active Process' },
|
|
3799
|
+
{ name: 'VALIDATION', label: '03 / VERIFY', desc: 'Quality Gate' },
|
|
3800
|
+
{ name: 'DEPLOYMENT', label: '04 / OUTPUT', desc: 'Master Release' },
|
|
3801
|
+
];
|
|
3802
|
+
|
|
3803
|
+
const pipeY = cy;
|
|
3804
|
+
const startX = cx - 450;
|
|
3805
|
+
const totalW = 900;
|
|
3806
|
+
const segW = totalW / (stages.length - 1);
|
|
3807
|
+
|
|
3808
|
+
ctx.save();
|
|
3809
|
+
const pScale = Math.min(1, Math.max(0, pipeSpring));
|
|
3810
|
+
ctx.translate(cx, cy);
|
|
3811
|
+
ctx.scale(pScale, pScale);
|
|
3812
|
+
ctx.translate(-cx, -cy);
|
|
3813
|
+
|
|
3814
|
+
// Main Conduit Pipeline Track
|
|
3815
|
+
const conduitG = ctx.createLinearGradient(startX, 0, startX + totalW, 0);
|
|
3816
|
+
conduitG.addColorStop(0, ACCENT);
|
|
3817
|
+
conduitG.addColorStop(0.5, '#38bdf8');
|
|
3818
|
+
conduitG.addColorStop(1, '#10b981');
|
|
3819
|
+
|
|
3820
|
+
ctx.strokeStyle = IS_LIGHT ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.15)';
|
|
3821
|
+
ctx.lineWidth = 14;
|
|
3822
|
+
ctx.lineCap = 'round';
|
|
3823
|
+
ctx.beginPath();
|
|
3824
|
+
ctx.moveTo(startX, pipeY);
|
|
3825
|
+
ctx.lineTo(startX + totalW, pipeY);
|
|
3826
|
+
ctx.stroke();
|
|
3827
|
+
|
|
3828
|
+
ctx.strokeStyle = conduitG;
|
|
3829
|
+
ctx.lineWidth = 6;
|
|
3830
|
+
ctx.stroke();
|
|
3831
|
+
|
|
3832
|
+
// Flowing Data Packet Orbs along the conduit
|
|
3833
|
+
for (let pk = 0; pk < 5; pk++) {
|
|
3834
|
+
const pkProgress = ((t * 0.45 + pk * 0.22) % 1.0);
|
|
3835
|
+
const pkX = startX + pkProgress * totalW;
|
|
3836
|
+
ctx.fillStyle = '#ffffff';
|
|
3837
|
+
ctx.beginPath();
|
|
3838
|
+
ctx.arc(pkX, pipeY, 6, 0, Math.PI * 2);
|
|
3839
|
+
ctx.fill();
|
|
3840
|
+
|
|
3841
|
+
ctx.fillStyle = ACCENT;
|
|
3842
|
+
ctx.globalAlpha = 0.4;
|
|
3843
|
+
ctx.beginPath();
|
|
3844
|
+
ctx.arc(pkX, pipeY, 14, 0, Math.PI * 2);
|
|
3845
|
+
ctx.fill();
|
|
3846
|
+
ctx.globalAlpha = 1.0;
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
// Stage Nodes & Info Cards
|
|
3850
|
+
for (let s = 0; s < stages.length; s++) {
|
|
3851
|
+
const st = stages[s];
|
|
3852
|
+
const nodeX = startX + s * segW;
|
|
3853
|
+
const nodeSpring = spring(t, 0.15 + s * 0.16, 180, 14);
|
|
3854
|
+
const nodeAlpha = Math.min(1, Math.max(0, nodeSpring));
|
|
3855
|
+
|
|
3856
|
+
const activeStage = Math.min(stages.length - 1, Math.floor((t / window.DURATION) * stages.length));
|
|
3857
|
+
const isActive = s === activeStage;
|
|
3858
|
+
|
|
3859
|
+
ctx.save();
|
|
3860
|
+
ctx.globalAlpha = nodeAlpha;
|
|
3861
|
+
ctx.translate(nodeX, pipeY);
|
|
3862
|
+
|
|
3863
|
+
const isTop = s % 2 === 0;
|
|
3864
|
+
const cardOffsetY = isTop ? -180 : 70;
|
|
3865
|
+
const cardW = 190;
|
|
3866
|
+
const cardH = 95;
|
|
3867
|
+
|
|
3868
|
+
sLine(0, 0, 0, isTop ? -50 : 50, 500 + s, isActive ? ACCENT : STROKE_ALT, 2.5);
|
|
3869
|
+
|
|
3870
|
+
ctx.fillStyle = CARD_BG;
|
|
3871
|
+
ctx.strokeStyle = isActive ? ACCENT : CARD_BORDER;
|
|
3872
|
+
ctx.lineWidth = isActive ? 3 : 1.5;
|
|
3873
|
+
ctx.beginPath();
|
|
3874
|
+
ctx.roundRect(-cardW / 2, cardOffsetY, cardW, cardH, 14);
|
|
3875
|
+
ctx.fill();
|
|
3876
|
+
ctx.stroke();
|
|
3877
|
+
|
|
3878
|
+
if (isActive) {
|
|
3879
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
|
|
3880
|
+
ctx.lineWidth = 1;
|
|
3881
|
+
ctx.beginPath();
|
|
3882
|
+
ctx.roundRect(-cardW / 2 + 4, cardOffsetY + 4, cardW - 8, cardH - 8, 10);
|
|
3883
|
+
ctx.stroke();
|
|
3884
|
+
}
|
|
3885
|
+
|
|
3886
|
+
ctx.textAlign = 'center';
|
|
3887
|
+
ctx.font = 'bold 12px ' + FONT_DISPLAY;
|
|
3888
|
+
ctx.fillStyle = isActive ? ACCENT : '#94a3b8';
|
|
3889
|
+
ctx.fillText(st.label, 0, cardOffsetY + 26);
|
|
3890
|
+
|
|
3891
|
+
ctx.font = '800 16px ' + FONT_DISPLAY;
|
|
3892
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
3893
|
+
ctx.fillText(st.name, 0, cardOffsetY + 52);
|
|
3894
|
+
|
|
3895
|
+
ctx.font = '500 13px ' + FONT_BODY;
|
|
3896
|
+
ctx.fillStyle = TEXT_MUTED;
|
|
3897
|
+
ctx.fillText(st.desc, 0, cardOffsetY + 76);
|
|
3898
|
+
|
|
3899
|
+
const nodeR = isActive ? 22 : 16;
|
|
3900
|
+
ctx.fillStyle = IS_LIGHT ? '#ffffff' : '#0f172a';
|
|
3901
|
+
ctx.beginPath();
|
|
3902
|
+
ctx.arc(0, 0, nodeR, 0, Math.PI * 2);
|
|
3903
|
+
ctx.fill();
|
|
3904
|
+
|
|
3905
|
+
ctx.strokeStyle = isActive ? ACCENT : '#64748b';
|
|
3906
|
+
ctx.lineWidth = isActive ? 4 : 3;
|
|
3907
|
+
ctx.beginPath();
|
|
3908
|
+
ctx.arc(0, 0, nodeR, 0, Math.PI * 2);
|
|
3909
|
+
ctx.stroke();
|
|
3910
|
+
|
|
3911
|
+
sCircle(0, 0, isActive ? 8 : 5, 520 + s, isActive ? '#ffffff' : '#94a3b8', 2, isActive ? ACCENT : '#64748b');
|
|
3912
|
+
|
|
3913
|
+
ctx.restore();
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3916
|
+
ctx.restore();
|
|
3917
|
+
|
|
3918
|
+
} else if (COMP_THEME === 'cognitive_insight') {
|
|
3919
|
+
// Universal Archetype: Cognitive Psychology, Neural Eureka, Theory of Mind & Idea Spark
|
|
3920
|
+
const brainSpring = spring(t, 0.05, 170, 14);
|
|
3921
|
+
const eurekaSpring = spring(t, window.DURATION * 0.35, 160, 12);
|
|
3922
|
+
const orbitSpring = spring(t, 0.40, 180, 15);
|
|
3923
|
+
|
|
3924
|
+
ctx.save();
|
|
3925
|
+
const cScale = Math.min(1, Math.max(0, brainSpring));
|
|
3926
|
+
ctx.translate(cx, cy);
|
|
3927
|
+
ctx.scale(cScale, cScale);
|
|
3928
|
+
|
|
3929
|
+
// Concentric Thought Wave Ripples
|
|
3930
|
+
for (let r = 0; r < 4; r++) {
|
|
3931
|
+
const ripPhase = (t * 0.8 + r * 0.25) % 1.0;
|
|
3932
|
+
const ripR = 90 + ripPhase * 240;
|
|
3933
|
+
ctx.strokeStyle = ACCENT;
|
|
3934
|
+
ctx.globalAlpha = Math.max(0, (1 - ripPhase) * 0.35);
|
|
3935
|
+
ctx.lineWidth = 2.5;
|
|
3936
|
+
ctx.beginPath();
|
|
3937
|
+
ctx.arc(0, 0, ripR, 0, Math.PI * 2);
|
|
3938
|
+
ctx.stroke();
|
|
3939
|
+
}
|
|
3940
|
+
ctx.globalAlpha = 1.0;
|
|
3941
|
+
|
|
3942
|
+
// Cerebral Neural Core / Brain Silhouette
|
|
3943
|
+
const brainR = 120;
|
|
3944
|
+
const coreGrad = ctx.createRadialGradient(0, -20, 20, 0, 0, brainR * 1.5);
|
|
3945
|
+
coreGrad.addColorStop(0, '#fef08a');
|
|
3946
|
+
coreGrad.addColorStop(0.3, ACCENT);
|
|
3947
|
+
coreGrad.addColorStop(0.8, '#6366f1');
|
|
3948
|
+
coreGrad.addColorStop(1, 'rgba(15, 23, 42, 0)');
|
|
3949
|
+
|
|
3950
|
+
ctx.fillStyle = coreGrad;
|
|
3951
|
+
ctx.beginPath();
|
|
3952
|
+
ctx.arc(0, 0, brainR * 1.4, 0, Math.PI * 2);
|
|
3953
|
+
ctx.fill();
|
|
3954
|
+
|
|
3955
|
+
// Neural Synapses
|
|
3956
|
+
const synNodes = [
|
|
3957
|
+
{ x: -50, y: -60 }, { x: 0, y: -80 }, { x: 50, y: -60 },
|
|
3958
|
+
{ x: -80, y: -20 }, { x: -30, y: -20 }, { x: 30, y: -20 }, { x: 80, y: -20 },
|
|
3959
|
+
{ x: -60, y: 30 }, { x: 0, y: 25 }, { x: 60, y: 30 },
|
|
3960
|
+
{ x: -25, y: 70 }, { x: 25, y: 70 },
|
|
3961
|
+
];
|
|
3962
|
+
|
|
3963
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.35)';
|
|
3964
|
+
ctx.lineWidth = 2;
|
|
3965
|
+
for (let i = 0; i < synNodes.length; i++) {
|
|
3966
|
+
for (let j = i + 1; j < synNodes.length; j++) {
|
|
3967
|
+
const dx = synNodes[i].x - synNodes[j].x;
|
|
3968
|
+
const dy = synNodes[i].y - synNodes[j].y;
|
|
3969
|
+
const dist = Math.hypot(dx, dy);
|
|
3970
|
+
if (dist < 75) {
|
|
3971
|
+
ctx.beginPath();
|
|
3972
|
+
ctx.moveTo(synNodes[i].x, synNodes[i].y);
|
|
3973
|
+
ctx.lineTo(synNodes[j].x, synNodes[j].y);
|
|
3974
|
+
ctx.stroke();
|
|
3975
|
+
|
|
3976
|
+
const pulse = (t * 2.5 + i * 0.3 + j * 0.2) % 1.0;
|
|
3977
|
+
const px = synNodes[i].x + (synNodes[j].x - synNodes[i].x) * pulse;
|
|
3978
|
+
const py = synNodes[i].y + (synNodes[j].y - synNodes[i].y) * pulse;
|
|
3979
|
+
ctx.fillStyle = '#ffffff';
|
|
3980
|
+
ctx.beginPath();
|
|
3981
|
+
ctx.arc(px, py, 2.5, 0, Math.PI * 2);
|
|
3982
|
+
ctx.fill();
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
|
|
3987
|
+
for (let n = 0; n < synNodes.length; n++) {
|
|
3988
|
+
const sn = synNodes[n];
|
|
3989
|
+
const fire = Math.sin(t * 8 + n * 1.3);
|
|
3990
|
+
const dotR = 5 + (fire > 0.5 ? 2.5 : 0);
|
|
3991
|
+
sCircle(sn.x, sn.y, dotR, 600 + n, '#ffffff', 2, fire > 0.3 ? '#fef08a' : ACCENT);
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3994
|
+
// Eureka Idea Spark Burst
|
|
3995
|
+
if (eurekaSpring > 0.05) {
|
|
3996
|
+
const eProgress = Math.min(1, Math.max(0, eurekaSpring));
|
|
3997
|
+
ctx.save();
|
|
3998
|
+
ctx.scale(eProgress, eProgress);
|
|
3999
|
+
|
|
4000
|
+
const rays = 12;
|
|
4001
|
+
for (let ry = 0; ry < rays; ry++) {
|
|
4002
|
+
const ang = (ry * Math.PI * 2) / rays + t * 0.4;
|
|
4003
|
+
const len = 140 + Math.sin(t * 6 + ry) * 25;
|
|
4004
|
+
ctx.strokeStyle = ry % 2 === 0 ? '#fef08a' : '#ffffff';
|
|
4005
|
+
ctx.lineWidth = ry % 2 === 0 ? 3 : 1.5;
|
|
4006
|
+
ctx.beginPath();
|
|
4007
|
+
ctx.moveTo(Math.cos(ang) * 50, Math.sin(ang) * 50);
|
|
4008
|
+
ctx.lineTo(Math.cos(ang) * len, Math.sin(ang) * len);
|
|
4009
|
+
ctx.stroke();
|
|
4010
|
+
}
|
|
4011
|
+
|
|
4012
|
+
sCircle(0, 0, 18, 650, '#ffffff', 3, '#f59e0b');
|
|
4013
|
+
sCircle(0, 0, 8, 651, '#ffffff', 2, '#ffffff');
|
|
4014
|
+
ctx.restore();
|
|
4015
|
+
}
|
|
4016
|
+
|
|
4017
|
+
// Orbiting Insight Badges
|
|
4018
|
+
if (orbitSpring > 0.05) {
|
|
4019
|
+
const badges = ['PERCEPTION', 'COGNITION', 'INTUITION', 'INSIGHT'];
|
|
4020
|
+
ctx.save();
|
|
4021
|
+
ctx.globalAlpha = Math.min(1, Math.max(0, orbitSpring));
|
|
4022
|
+
|
|
4023
|
+
for (let bg = 0; bg < badges.length; bg++) {
|
|
4024
|
+
const bgAngle = (bg * Math.PI * 2) / badges.length + t * 0.35;
|
|
4025
|
+
const bgX = Math.cos(bgAngle) * 310;
|
|
4026
|
+
const bgY = Math.sin(bgAngle) * 190;
|
|
4027
|
+
|
|
4028
|
+
ctx.fillStyle = CARD_BG;
|
|
4029
|
+
ctx.strokeStyle = CARD_BORDER;
|
|
4030
|
+
ctx.lineWidth = 1.5;
|
|
4031
|
+
ctx.beginPath();
|
|
4032
|
+
ctx.roundRect(bgX - 65, bgY - 18, 130, 36, 18);
|
|
4033
|
+
ctx.fill();
|
|
4034
|
+
ctx.stroke();
|
|
4035
|
+
|
|
4036
|
+
ctx.font = 'bold 13px ' + FONT_DISPLAY;
|
|
4037
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
4038
|
+
ctx.textAlign = 'center';
|
|
4039
|
+
ctx.fillText(badges[bg], bgX, bgY + 5);
|
|
4040
|
+
|
|
4041
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.12)';
|
|
4042
|
+
ctx.beginPath();
|
|
4043
|
+
ctx.moveTo(0, 0);
|
|
4044
|
+
ctx.lineTo(bgX, bgY);
|
|
4045
|
+
ctx.stroke();
|
|
4046
|
+
}
|
|
4047
|
+
ctx.restore();
|
|
4048
|
+
}
|
|
4049
|
+
|
|
4050
|
+
ctx.restore();
|
|
4051
|
+
|
|
4052
|
+
} else if (COMP_THEME === 'magnification_focus') {
|
|
4053
|
+
// Universal Archetype: Optical Loupe, Precision Macro Zoom & Scrutiny Reticle
|
|
4054
|
+
const focusSpring = spring(t, 0.05, 170, 14);
|
|
4055
|
+
const loupeSpring = spring(t, 0.25, 180, 14);
|
|
4056
|
+
|
|
4057
|
+
// Deep research background grid & underlying specimen traces
|
|
4058
|
+
ctx.save();
|
|
4059
|
+
ctx.strokeStyle = IS_LIGHT ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.08)';
|
|
4060
|
+
ctx.lineWidth = 1;
|
|
4061
|
+
for (let gx = 0; gx < W; gx += 40) {
|
|
4062
|
+
ctx.beginPath(); ctx.moveTo(gx, 0); ctx.lineTo(gx, H); ctx.stroke();
|
|
4063
|
+
}
|
|
4064
|
+
for (let gy = 0; gy < H; gy += 40) {
|
|
4065
|
+
ctx.beginPath(); ctx.moveTo(0, gy); ctx.lineTo(W, gy); ctx.stroke();
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
for (let tr = 0; tr < 16; tr++) {
|
|
4069
|
+
const trX = cx - 350 + tr * 48;
|
|
4070
|
+
const trY = cy - 200 + ((tr * 73) % 400);
|
|
4071
|
+
sLine(trX, trY, trX + 80, trY + 40, 700 + tr, IS_LIGHT ? 'rgba(0,0,0,0.15)' : 'rgba(56, 189, 248, 0.2)', 2);
|
|
4072
|
+
}
|
|
4073
|
+
ctx.restore();
|
|
4074
|
+
|
|
4075
|
+
// Floating Optical Loupe (Lissajous gentle inspection movement)
|
|
4076
|
+
const loupeR = Math.min(W, H) * 0.26;
|
|
4077
|
+
const lissX = cx + Math.sin(t * 1.1) * 70;
|
|
4078
|
+
const lissY = cy + Math.cos(t * 0.85) * 45;
|
|
4079
|
+
|
|
4080
|
+
ctx.save();
|
|
4081
|
+
const lScale = Math.min(1, Math.max(0, loupeSpring));
|
|
4082
|
+
ctx.translate(lissX, lissY);
|
|
4083
|
+
ctx.scale(lScale, lScale);
|
|
4084
|
+
|
|
4085
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.45)';
|
|
4086
|
+
ctx.beginPath();
|
|
4087
|
+
ctx.arc(15, 20, loupeR, 0, Math.PI * 2);
|
|
4088
|
+
ctx.fill();
|
|
4089
|
+
|
|
4090
|
+
// Inside Magnified Specimen (Clipped Circle)
|
|
4091
|
+
ctx.save();
|
|
4092
|
+
ctx.beginPath();
|
|
4093
|
+
ctx.arc(0, 0, loupeR - 8, 0, Math.PI * 2);
|
|
4094
|
+
ctx.clip();
|
|
4095
|
+
|
|
4096
|
+
ctx.fillStyle = IS_LIGHT ? '#ffffff' : '#070f1e';
|
|
4097
|
+
ctx.fillRect(-loupeR, -loupeR, loupeR * 2, loupeR * 2);
|
|
4098
|
+
|
|
4099
|
+
ctx.save();
|
|
4100
|
+
ctx.scale(2.4, 2.4);
|
|
4101
|
+
for (let m = -8; m <= 8; m++) {
|
|
4102
|
+
sLine(m * 22, -loupeR, m * 22, loupeR, 720 + m, 'rgba(56, 189, 248, 0.3)', 1.5);
|
|
4103
|
+
sLine(-loupeR, m * 22, loupeR, m * 22, 740 + m, 'rgba(56, 189, 248, 0.3)', 1.5);
|
|
4104
|
+
}
|
|
4105
|
+
sCircle(0, 0, 36, 760, '#f59e0b', 3, 'rgba(245, 158, 11, 0.2)');
|
|
4106
|
+
sCircle(0, 0, 16, 761, '#ef4444', 2.5, '#ef4444');
|
|
4107
|
+
for (let br = 0; br < 6; br++) {
|
|
4108
|
+
const brA = (br * Math.PI * 2) / 6 + t * 0.5;
|
|
4109
|
+
sLine(0, 0, Math.cos(brA) * 55, Math.sin(brA) * 55, 770 + br, ACCENT, 2.5);
|
|
4110
|
+
sCircle(Math.cos(brA) * 55, Math.sin(brA) * 55, 5, 780 + br, '#ffffff', 1.5, ACCENT);
|
|
4111
|
+
}
|
|
4112
|
+
ctx.restore();
|
|
4113
|
+
|
|
4114
|
+
// Precision Crosshairs & Calibrated Reticle
|
|
4115
|
+
ctx.strokeStyle = '#38bdf8';
|
|
4116
|
+
ctx.lineWidth = 1.5;
|
|
4117
|
+
ctx.beginPath();
|
|
4118
|
+
ctx.moveTo(-loupeR + 15, 0); ctx.lineTo(-25, 0);
|
|
4119
|
+
ctx.moveTo(25, 0); ctx.lineTo(loupeR - 15, 0);
|
|
4120
|
+
ctx.moveTo(0, -loupeR + 15); ctx.lineTo(0, -25);
|
|
4121
|
+
ctx.moveTo(0, 25); ctx.lineTo(0, loupeR - 15);
|
|
4122
|
+
ctx.stroke();
|
|
4123
|
+
|
|
4124
|
+
for (let deg = 0; deg < 36; deg++) {
|
|
4125
|
+
const dRad = (deg * Math.PI * 2) / 36;
|
|
4126
|
+
const isMajor = deg % 9 === 0;
|
|
4127
|
+
const tLen = isMajor ? 16 : 8;
|
|
4128
|
+
const rOut = loupeR - 12;
|
|
4129
|
+
const rIn = rOut - tLen;
|
|
4130
|
+
ctx.strokeStyle = isMajor ? ACCENT : 'rgba(255,255,255,0.4)';
|
|
4131
|
+
ctx.lineWidth = isMajor ? 2 : 1;
|
|
4132
|
+
ctx.beginPath();
|
|
4133
|
+
ctx.moveTo(Math.cos(dRad) * rIn, Math.sin(dRad) * rIn);
|
|
4134
|
+
ctx.lineTo(Math.cos(dRad) * rOut, Math.sin(dRad) * rOut);
|
|
4135
|
+
ctx.stroke();
|
|
4136
|
+
}
|
|
4137
|
+
ctx.restore(); // End clipping
|
|
4138
|
+
|
|
4139
|
+
// Bezel
|
|
4140
|
+
ctx.strokeStyle = '#d4af37';
|
|
4141
|
+
ctx.lineWidth = 14;
|
|
4142
|
+
ctx.beginPath();
|
|
4143
|
+
ctx.arc(0, 0, loupeR, 0, Math.PI * 2);
|
|
4144
|
+
ctx.stroke();
|
|
4145
|
+
|
|
4146
|
+
ctx.strokeStyle = '#fef08a';
|
|
4147
|
+
ctx.lineWidth = 2.5;
|
|
4148
|
+
ctx.beginPath();
|
|
4149
|
+
ctx.arc(0, 0, loupeR - 6, 0, Math.PI * 2);
|
|
4150
|
+
ctx.stroke();
|
|
4151
|
+
|
|
4152
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
|
|
4153
|
+
ctx.lineWidth = 4;
|
|
4154
|
+
ctx.beginPath();
|
|
4155
|
+
ctx.arc(0, 0, loupeR - 24, -1.8, -0.6);
|
|
4156
|
+
ctx.stroke();
|
|
4157
|
+
|
|
4158
|
+
ctx.fillStyle = CARD_BG;
|
|
4159
|
+
ctx.strokeStyle = ACCENT;
|
|
4160
|
+
ctx.lineWidth = 1.5;
|
|
4161
|
+
ctx.beginPath();
|
|
4162
|
+
ctx.roundRect(loupeR - 40, -loupeR - 10, 160, 48, 10);
|
|
4163
|
+
ctx.fill();
|
|
4164
|
+
ctx.stroke();
|
|
4165
|
+
|
|
4166
|
+
ctx.font = 'bold 12px monospace';
|
|
4167
|
+
ctx.fillStyle = ACCENT;
|
|
4168
|
+
ctx.fillText('MAGNIFY: 250%', loupeR - 26, -loupeR + 12);
|
|
4169
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
4170
|
+
ctx.fillText('POS: ' + Math.round(lissX) + ', ' + Math.round(lissY), loupeR - 26, -loupeR + 28);
|
|
4171
|
+
|
|
4172
|
+
ctx.restore();
|
|
4173
|
+
|
|
4174
|
+
} else if (COMP_THEME === 'waveform') {
|
|
3317
4175
|
// Kinetic Frequency Equalizer Waveform
|
|
3318
4176
|
const bars = 36;
|
|
3319
4177
|
const barW = (W * 0.7) / bars;
|
|
@@ -3330,6 +4188,153 @@ function drawMainScene(t) {
|
|
|
3330
4188
|
ctx.fillStyle = barGrad;
|
|
3331
4189
|
ctx.beginPath(); ctx.roundRect(bx, by, barW - 6, hVal, 4); ctx.fill();
|
|
3332
4190
|
}
|
|
4191
|
+
|
|
4192
|
+
} else {
|
|
4193
|
+
// Universal Semantic Default & Explainer Presentation (Whiteboard Masterclass & Concept Pillars)
|
|
4194
|
+
const boardSpring = spring(t, 0.05, 170, 14);
|
|
4195
|
+
const p1Spring = spring(t, 0.18, 190, 15);
|
|
4196
|
+
const p2Spring = spring(t, 0.32, 190, 15);
|
|
4197
|
+
const p3Spring = spring(t, 0.46, 190, 15);
|
|
4198
|
+
|
|
4199
|
+
const boardW = Math.min(W * 0.88, 1100);
|
|
4200
|
+
const boardH = Math.min(H * 0.68, 620);
|
|
4201
|
+
const boardX = cx - boardW / 2;
|
|
4202
|
+
const boardY = cy - boardH / 2;
|
|
4203
|
+
|
|
4204
|
+
ctx.save();
|
|
4205
|
+
const bScale = Math.min(1, Math.max(0, boardSpring));
|
|
4206
|
+
ctx.translate(cx, cy);
|
|
4207
|
+
ctx.scale(bScale, bScale);
|
|
4208
|
+
ctx.translate(-cx, -cy);
|
|
4209
|
+
|
|
4210
|
+
// Architectural Frosted Glass Presentation Board
|
|
4211
|
+
ctx.fillStyle = CARD_BG;
|
|
4212
|
+
ctx.strokeStyle = CARD_BORDER;
|
|
4213
|
+
ctx.lineWidth = 2.5;
|
|
4214
|
+
ctx.beginPath();
|
|
4215
|
+
ctx.roundRect(boardX, boardY, boardW, boardH, 24);
|
|
4216
|
+
ctx.fill();
|
|
4217
|
+
ctx.stroke();
|
|
4218
|
+
|
|
4219
|
+
// Board Header Accent Bar
|
|
4220
|
+
ctx.fillStyle = ACCENT;
|
|
4221
|
+
ctx.beginPath();
|
|
4222
|
+
ctx.roundRect(boardX + 32, boardY + 28, 6, 32, 3);
|
|
4223
|
+
ctx.fill();
|
|
4224
|
+
|
|
4225
|
+
ctx.font = '800 22px ' + FONT_DISPLAY;
|
|
4226
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
4227
|
+
ctx.textAlign = 'left';
|
|
4228
|
+
ctx.fillText('CORE PRINCIPLES & ARCHITECTURE', boardX + 48, boardY + 50);
|
|
4229
|
+
|
|
4230
|
+
// Live Status Badge
|
|
4231
|
+
ctx.fillStyle = 'rgba(16, 185, 129, 0.15)';
|
|
4232
|
+
ctx.beginPath();
|
|
4233
|
+
ctx.roundRect(boardX + boardW - 160, boardY + 28, 128, 30, 15);
|
|
4234
|
+
ctx.fill();
|
|
4235
|
+
ctx.fillStyle = '#10b981';
|
|
4236
|
+
ctx.font = 'bold 13px ' + FONT_DISPLAY;
|
|
4237
|
+
ctx.fillText('● SYSTEM ACTIVE', boardX + boardW - 146, boardY + 48);
|
|
4238
|
+
|
|
4239
|
+
// 3 Structured Concept Pillars
|
|
4240
|
+
const pillarData = [
|
|
4241
|
+
{ num: '01', title: 'FOUNDATION', desc: 'Core axioms & structure', icon: 'diamond', spr: p1Spring },
|
|
4242
|
+
{ num: '02', title: 'DYNAMICS', desc: 'Kinetic transformation', icon: 'gear', spr: p2Spring },
|
|
4243
|
+
{ num: '03', title: 'IMPACT', desc: 'Synthesized outcome', icon: 'star', spr: p3Spring },
|
|
4244
|
+
];
|
|
4245
|
+
|
|
4246
|
+
const pillarW = (boardW - 128) / 3;
|
|
4247
|
+
const pillarH = boardH - 140;
|
|
4248
|
+
const pillarY = boardY + 95;
|
|
4249
|
+
|
|
4250
|
+
for (let p = 0; p < 3; p++) {
|
|
4251
|
+
const pd = pillarData[p];
|
|
4252
|
+
const pilX = boardX + 32 + p * (pillarW + 32);
|
|
4253
|
+
const pAlpha = Math.min(1, Math.max(0, pd.spr));
|
|
4254
|
+
|
|
4255
|
+
ctx.save();
|
|
4256
|
+
ctx.globalAlpha = pAlpha;
|
|
4257
|
+
ctx.translate(pilX, pillarY + (1 - pd.spr) * 40);
|
|
4258
|
+
|
|
4259
|
+
// Pillar Card
|
|
4260
|
+
ctx.fillStyle = IS_LIGHT ? 'rgba(255,255,255,0.7)' : 'rgba(255,255,255,0.04)';
|
|
4261
|
+
ctx.strokeStyle = p === 1 ? ACCENT : 'rgba(255,255,255,0.1)';
|
|
4262
|
+
ctx.lineWidth = p === 1 ? 2.5 : 1.5;
|
|
4263
|
+
ctx.beginPath();
|
|
4264
|
+
ctx.roundRect(0, 0, pillarW, pillarH, 16);
|
|
4265
|
+
ctx.fill();
|
|
4266
|
+
ctx.stroke();
|
|
4267
|
+
|
|
4268
|
+
// Number Tag
|
|
4269
|
+
ctx.font = '900 48px ' + FONT_DISPLAY;
|
|
4270
|
+
ctx.fillStyle = p === 1 ? ACCENT : (IS_LIGHT ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.12)');
|
|
4271
|
+
ctx.fillText(pd.num, 24, 60);
|
|
4272
|
+
|
|
4273
|
+
// Pillar Title
|
|
4274
|
+
ctx.font = '700 20px ' + FONT_DISPLAY;
|
|
4275
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
4276
|
+
ctx.fillText(pd.title, 24, 110);
|
|
4277
|
+
|
|
4278
|
+
// Pillar Description
|
|
4279
|
+
ctx.font = '500 15px ' + FONT_BODY;
|
|
4280
|
+
ctx.fillStyle = TEXT_MUTED;
|
|
4281
|
+
ctx.fillText(pd.desc, 24, 138);
|
|
4282
|
+
|
|
4283
|
+
// Dynamic Animated Diagram Graphic inside Pillar
|
|
4284
|
+
const gfxY = pillarH * 0.65;
|
|
4285
|
+
const gfxX = pillarW / 2;
|
|
4286
|
+
|
|
4287
|
+
if (pd.icon === 'diamond') {
|
|
4288
|
+
ctx.save();
|
|
4289
|
+
ctx.translate(gfxX, gfxY);
|
|
4290
|
+
ctx.rotate(t * 1.2);
|
|
4291
|
+
ctx.strokeStyle = ACCENT;
|
|
4292
|
+
ctx.lineWidth = 3;
|
|
4293
|
+
ctx.strokeRect(-28, -28, 56, 56);
|
|
4294
|
+
sCircle(0, 0, 10, 800, '#ffffff', 2, ACCENT);
|
|
4295
|
+
ctx.restore();
|
|
4296
|
+
} else if (pd.icon === 'gear') {
|
|
4297
|
+
sCircle(gfxX, gfxY, 36, 810, ACCENT, 3);
|
|
4298
|
+
const pulseR = 20 + Math.sin(t * 5) * 8;
|
|
4299
|
+
sCircle(gfxX, gfxY, pulseR, 811, '#ffffff', 2, '#38bdf8');
|
|
4300
|
+
} else {
|
|
4301
|
+
for (let st = 0; st < 4; st++) {
|
|
4302
|
+
const ang = (st * Math.PI) / 2 + t * 0.8;
|
|
4303
|
+
sLine(gfxX, gfxY, gfxX + Math.cos(ang) * 36, gfxY + Math.sin(ang) * 36, 820 + st, '#f59e0b', 3);
|
|
4304
|
+
}
|
|
4305
|
+
sCircle(gfxX, gfxY, 8, 825, '#ffffff', 2, '#f59e0b');
|
|
4306
|
+
}
|
|
4307
|
+
|
|
4308
|
+
// Feature Check items
|
|
4309
|
+
for (let ch = 0; ch < 3; ch++) {
|
|
4310
|
+
const itemY = pillarH - 85 + ch * 24;
|
|
4311
|
+
sCircle(28, itemY, 4, 830 + ch, ACCENT, 2, ACCENT);
|
|
4312
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
4313
|
+
ctx.font = '500 13px ' + FONT_BODY;
|
|
4314
|
+
ctx.fillText('Verification Key 0' + (ch + 1), 42, itemY + 4);
|
|
4315
|
+
}
|
|
4316
|
+
|
|
4317
|
+
ctx.restore();
|
|
4318
|
+
}
|
|
4319
|
+
|
|
4320
|
+
// Inter-Pillar Energy Connectors
|
|
4321
|
+
ctx.strokeStyle = ACCENT;
|
|
4322
|
+
ctx.lineWidth = 2.5;
|
|
4323
|
+
for (let c = 0; c < 2; c++) {
|
|
4324
|
+
const connX1 = boardX + 32 + (c + 1) * pillarW + c * 32 - 16;
|
|
4325
|
+
const connX2 = connX1 + 32;
|
|
4326
|
+
const connY = pillarY + 80;
|
|
4327
|
+
sLine(connX1, connY, connX2, connY, 850 + c, ACCENT, 3);
|
|
4328
|
+
ctx.fillStyle = ACCENT;
|
|
4329
|
+
ctx.beginPath();
|
|
4330
|
+
ctx.moveTo(connX2 + 4, connY);
|
|
4331
|
+
ctx.lineTo(connX2 - 4, connY - 5);
|
|
4332
|
+
ctx.lineTo(connX2 - 4, connY + 5);
|
|
4333
|
+
ctx.closePath();
|
|
4334
|
+
ctx.fill();
|
|
4335
|
+
}
|
|
4336
|
+
|
|
4337
|
+
ctx.restore();
|
|
3333
4338
|
}
|
|
3334
4339
|
|
|
3335
4340
|
ctx.restore();
|
|
@@ -3481,6 +4486,7 @@ module.exports = {
|
|
|
3481
4486
|
detectAesthetic,
|
|
3482
4487
|
detectComponentTheme,
|
|
3483
4488
|
detectCaptionsRequested,
|
|
4489
|
+
detectAnchorRequested,
|
|
3484
4490
|
getScriptFontStack,
|
|
3485
4491
|
AESTHETICS,
|
|
3486
4492
|
REGIONAL_FONTS,
|