(beta) butterchurn visualizer
This commit is contained in:
parent
c484148078
commit
cc6c600817
6 changed files with 2981 additions and 3756 deletions
6367
index.html
6367
index.html
File diff suppressed because one or more lines are too long
|
|
@ -635,6 +635,7 @@ export const visualizerSettings = {
|
|||
ENABLED_KEY: 'visualizer-enabled',
|
||||
MODE_KEY: 'visualizer-mode', // 'solid' or 'blended'
|
||||
PRESET_KEY: 'visualizer-preset',
|
||||
BUTTERCHURN_CYCLE_KEY: 'butterchurn-cycle-duration',
|
||||
|
||||
getPreset() {
|
||||
try {
|
||||
|
|
@ -699,6 +700,20 @@ export const visualizerSettings = {
|
|||
setSmartIntensity(enabled) {
|
||||
localStorage.setItem(this.SMART_INTENSITY_KEY, enabled);
|
||||
},
|
||||
|
||||
// Butterchurn preset cycle duration in seconds (0 = disabled)
|
||||
getButterchurnCycleDuration() {
|
||||
try {
|
||||
const val = localStorage.getItem(this.BUTTERCHURN_CYCLE_KEY);
|
||||
return val ? parseInt(val, 10) : 30;
|
||||
} catch {
|
||||
return 30;
|
||||
}
|
||||
},
|
||||
|
||||
setButterchurnCycleDuration(seconds) {
|
||||
localStorage.setItem(this.BUTTERCHURN_CYCLE_KEY, seconds.toString());
|
||||
},
|
||||
};
|
||||
|
||||
export const equalizerSettings = {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { visualizerSettings } from './storage.js';
|
|||
import { LCDPreset } from './visualizers/lcd.js';
|
||||
import { ParticlesPreset } from './visualizers/particles.js';
|
||||
import { UnknownPleasuresWebGL } from './visualizers/unknown_pleasures_webgl.js';
|
||||
import { ButterchurnPreset } from './visualizers/butterchurn.js';
|
||||
import { audioContextManager } from './audio-context.js';
|
||||
|
||||
export class Visualizer {
|
||||
|
|
@ -21,6 +22,7 @@ export class Visualizer {
|
|||
lcd: new LCDPreset(),
|
||||
particles: new ParticlesPreset(),
|
||||
'unknown-pleasures': new UnknownPleasuresWebGL(),
|
||||
butterchurn: new ButterchurnPreset(),
|
||||
};
|
||||
|
||||
this.activePresetKey = visualizerSettings.getPreset();
|
||||
|
|
@ -139,6 +141,15 @@ export class Visualizer {
|
|||
this.audioContext.resume();
|
||||
}
|
||||
|
||||
// Initialize Butterchurn if it's the active preset
|
||||
if (this.activePresetKey === 'butterchurn' && this.activePreset.lazyInit) {
|
||||
this.activePreset.lazyInit(
|
||||
this.canvas,
|
||||
this.audioContext,
|
||||
audioContextManager.source
|
||||
);
|
||||
}
|
||||
|
||||
this.resize();
|
||||
window.addEventListener('resize', this._resizeBound);
|
||||
this.canvas.style.display = 'block';
|
||||
|
|
@ -258,5 +269,14 @@ export class Visualizer {
|
|||
this.activePresetKey = key;
|
||||
this.initContext();
|
||||
this.resize();
|
||||
|
||||
// Initialize Butterchurn if switching to it
|
||||
if (key === 'butterchurn' && this.presets[key].lazyInit && this.audioContext) {
|
||||
this.presets[key].lazyInit(
|
||||
this.canvas,
|
||||
this.audioContext,
|
||||
audioContextManager.source
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
259
js/visualizers/butterchurn.js
Normal file
259
js/visualizers/butterchurn.js
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
/**
|
||||
* Butterchurn (Milkdrop) Visualizer Preset
|
||||
* WebGL-based audio visualization using the Butterchurn library
|
||||
*/
|
||||
import butterchurn from 'butterchurn';
|
||||
import butterchurnPresets from 'butterchurn-presets';
|
||||
import { visualizerSettings } from '../storage.js';
|
||||
|
||||
export class ButterchurnPreset {
|
||||
constructor() {
|
||||
this.name = 'Butterchurn';
|
||||
this.contextType = 'webgl';
|
||||
|
||||
this.visualizer = null;
|
||||
this.canvas = null;
|
||||
this.audioContext = null;
|
||||
this.presets = null;
|
||||
this.presetKeys = [];
|
||||
this.currentPresetIndex = 0;
|
||||
this.lastPresetChange = 0;
|
||||
this.isInitialized = false;
|
||||
|
||||
// Transition settings
|
||||
this.blendProgress = 0;
|
||||
this.blendDuration = 2.7; // seconds for preset transitions
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the preset cycle duration from settings (in milliseconds)
|
||||
*/
|
||||
getPresetDuration() {
|
||||
const seconds = visualizerSettings.getButterchurnCycleDuration();
|
||||
return seconds * 1000; // Convert to milliseconds
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Butterchurn with the given WebGL context
|
||||
*/
|
||||
init(canvas, gl, audioContext, sourceNode) {
|
||||
if (this.isInitialized) return;
|
||||
|
||||
try {
|
||||
this.canvas = canvas;
|
||||
this.audioContext = audioContext;
|
||||
|
||||
// Load presets
|
||||
this.presets = butterchurnPresets.getPresets();
|
||||
this.presetKeys = Object.keys(this.presets);
|
||||
|
||||
// Filter to get a good selection of presets (some are better than others)
|
||||
this.presetKeys = this.presetKeys.filter(key => {
|
||||
// Skip some problematic or less visually appealing presets
|
||||
const skipPatterns = ['flexi', 'empty', 'test', '_'];
|
||||
return !skipPatterns.some(pattern => key.toLowerCase().includes(pattern));
|
||||
});
|
||||
|
||||
if (this.presetKeys.length === 0) {
|
||||
this.presetKeys = Object.keys(this.presets);
|
||||
}
|
||||
|
||||
// Shuffle presets for variety
|
||||
this.shufflePresets();
|
||||
|
||||
// Create Butterchurn visualizer
|
||||
this.visualizer = butterchurn.createVisualizer(audioContext, canvas, {
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
pixelRatio: window.devicePixelRatio || 1,
|
||||
textureRatio: 1,
|
||||
});
|
||||
|
||||
// Connect audio source
|
||||
if (sourceNode) {
|
||||
this.visualizer.connectAudio(sourceNode);
|
||||
}
|
||||
|
||||
// Load initial preset
|
||||
this.loadRandomPreset();
|
||||
|
||||
this.lastPresetChange = performance.now();
|
||||
this.isInitialized = true;
|
||||
|
||||
console.log('[Butterchurn] Initialized with', this.presetKeys.length, 'presets');
|
||||
} catch (error) {
|
||||
console.error('[Butterchurn] Initialization failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle the preset keys for random variety
|
||||
*/
|
||||
shufflePresets() {
|
||||
for (let i = this.presetKeys.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[this.presetKeys[i], this.presetKeys[j]] = [this.presetKeys[j], this.presetKeys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a random preset with smooth transition
|
||||
*/
|
||||
loadRandomPreset() {
|
||||
if (!this.visualizer || this.presetKeys.length === 0) return;
|
||||
|
||||
this.currentPresetIndex = (this.currentPresetIndex + 1) % this.presetKeys.length;
|
||||
const presetKey = this.presetKeys[this.currentPresetIndex];
|
||||
const preset = this.presets[presetKey];
|
||||
|
||||
if (preset) {
|
||||
try {
|
||||
this.visualizer.loadPreset(preset, this.blendDuration);
|
||||
console.log('[Butterchurn] Loaded preset:', presetKey);
|
||||
} catch (error) {
|
||||
console.warn('[Butterchurn] Failed to load preset:', presetKey, error);
|
||||
// Try next preset
|
||||
if (this.presetKeys.length > 1) {
|
||||
this.presetKeys.splice(this.currentPresetIndex, 1);
|
||||
this.loadRandomPreset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a specific preset by name
|
||||
*/
|
||||
loadPreset(presetName) {
|
||||
if (!this.visualizer || !this.presets) return;
|
||||
|
||||
const preset = this.presets[presetName];
|
||||
if (preset) {
|
||||
this.visualizer.loadPreset(preset, this.blendDuration);
|
||||
console.log('[Butterchurn] Loaded preset:', presetName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of available preset names
|
||||
*/
|
||||
getPresetNames() {
|
||||
return this.presetKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current preset name
|
||||
*/
|
||||
getCurrentPresetName() {
|
||||
return this.presetKeys[this.currentPresetIndex] || 'Unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip to next preset
|
||||
*/
|
||||
nextPreset() {
|
||||
this.loadRandomPreset();
|
||||
this.lastPresetChange = performance.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize handler
|
||||
*/
|
||||
resize(width, height) {
|
||||
if (this.visualizer) {
|
||||
this.visualizer.setRendererSize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main draw function called each animation frame
|
||||
*/
|
||||
draw(ctx, canvas, analyser, dataArray, params) {
|
||||
if (!this.isInitialized) {
|
||||
// Lazy initialization - need audio context and source node
|
||||
// This will be handled by the visualizer.js main class
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.visualizer) return;
|
||||
|
||||
const { mode } = params;
|
||||
const now = performance.now();
|
||||
|
||||
// Auto-cycle presets (if cycle duration > 0)
|
||||
const cycleDuration = this.getPresetDuration();
|
||||
if (cycleDuration > 0 && now - this.lastPresetChange > cycleDuration) {
|
||||
this.loadRandomPreset();
|
||||
this.lastPresetChange = now;
|
||||
}
|
||||
|
||||
// Render the visualization
|
||||
try {
|
||||
this.visualizer.render();
|
||||
} catch (error) {
|
||||
console.warn('[Butterchurn] Render error:', error);
|
||||
}
|
||||
|
||||
// Handle blended mode - we need to composite with cover art
|
||||
// Butterchurn renders directly to the canvas, so for blended mode
|
||||
// we need to adjust the canvas opacity/blend
|
||||
if (mode === 'blended') {
|
||||
// The canvas will be composited by CSS in the parent
|
||||
canvas.style.opacity = '0.85';
|
||||
canvas.style.mixBlendMode = 'screen';
|
||||
} else {
|
||||
canvas.style.opacity = '1';
|
||||
canvas.style.mixBlendMode = 'normal';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect audio source to the visualizer
|
||||
*/
|
||||
connectAudio(sourceNode) {
|
||||
if (this.visualizer && sourceNode) {
|
||||
try {
|
||||
this.visualizer.connectAudio(sourceNode);
|
||||
console.log('[Butterchurn] Audio connected');
|
||||
} catch (error) {
|
||||
console.warn('[Butterchurn] Failed to connect audio:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initialization helper for when audio context becomes available
|
||||
*/
|
||||
lazyInit(canvas, audioContext, sourceNode) {
|
||||
if (!this.isInitialized && canvas && audioContext) {
|
||||
const gl = canvas.getContext('webgl2', {
|
||||
alpha: true,
|
||||
antialias: true,
|
||||
preserveDrawingBuffer: true,
|
||||
}) || canvas.getContext('webgl', {
|
||||
alpha: true,
|
||||
antialias: true,
|
||||
preserveDrawingBuffer: true,
|
||||
});
|
||||
|
||||
if (gl) {
|
||||
this.init(canvas, gl, audioContext, sourceNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup resources
|
||||
*/
|
||||
destroy() {
|
||||
if (this.visualizer) {
|
||||
// Butterchurn doesn't have an explicit cleanup method
|
||||
// but we can null our references
|
||||
this.visualizer = null;
|
||||
}
|
||||
this.isInitialized = false;
|
||||
this.canvas = null;
|
||||
this.audioContext = null;
|
||||
console.log('[Butterchurn] Destroyed');
|
||||
}
|
||||
}
|
||||
70
package-lock.json
generated
70
package-lock.json
generated
|
|
@ -9,6 +9,8 @@
|
|||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"butterchurn": "^2.6.7",
|
||||
"butterchurn-presets": "^2.4.7",
|
||||
"dashjs": "^5.1.1",
|
||||
"pocketbase": "^0.26.5"
|
||||
},
|
||||
|
|
@ -74,6 +76,7 @@
|
|||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
|
|
@ -1513,7 +1516,6 @@
|
|||
"version": "7.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
|
||||
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
|
@ -1603,6 +1605,7 @@
|
|||
"integrity": "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@keyv/serialize": "^1.1.1"
|
||||
}
|
||||
|
|
@ -1644,6 +1647,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
|
|
@ -1687,6 +1691,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
|
|
@ -3138,6 +3143,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@svta/cml-xml/-/cml-xml-1.0.1.tgz",
|
||||
"integrity": "sha512-11LkJa5kDEcsRMWkVI1ABH3KLCxGoiSVe4kQ293ItVj8ncTTQ7htmCGiJDjS+Cmy35UgF3e/vc0ysJIiWRTx2g==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
|
|
@ -3186,6 +3192,7 @@
|
|||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
|
|
@ -3209,6 +3216,7 @@
|
|||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
|
|
@ -3397,6 +3405,16 @@
|
|||
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-runtime": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
"integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-js": "^2.4.0",
|
||||
"regenerator-runtime": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
|
@ -3496,6 +3514,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
|
|
@ -3517,6 +3536,27 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/butterchurn": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/butterchurn/-/butterchurn-2.6.7.tgz",
|
||||
"integrity": "sha512-BJiRA8L0L2+84uoG2SSfkp0kclBuN+vQKf217pK7pMlwEO2ZEg3MtO2/o+l8Qpr8Nbejg8tmL1ZHD1jmhiaaqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"ecma-proposal-math-extensions": "0.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/butterchurn-presets": {
|
||||
"version": "2.4.7",
|
||||
"resolved": "https://registry.npmjs.org/butterchurn-presets/-/butterchurn-presets-2.4.7.tgz",
|
||||
"integrity": "sha512-4MdM8ripz/VfH1BCldrIKdAc/1ryJFBDvqlyow6Ivo1frwj0H3duzvSMFC7/wIjAjxb1QpwVHVqGqS9uAFKhpg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.26.0",
|
||||
"ecma-proposal-math-extensions": "0.0.2",
|
||||
"lodash": "^4.17.4"
|
||||
}
|
||||
},
|
||||
"node_modules/cacheable": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.1.tgz",
|
||||
|
|
@ -3719,6 +3759,14 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
|
||||
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/core-js-compat": {
|
||||
"version": "3.47.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.47.0.tgz",
|
||||
|
|
@ -4006,6 +4054,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ecma-proposal-math-extensions": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ecma-proposal-math-extensions/-/ecma-proposal-math-extensions-0.0.2.tgz",
|
||||
"integrity": "sha512-80BnDp2Fn7RxXlEr5HHZblniY4aQ97MOAicdWWpSo0vkQiISSE9wLR4SqxKsu4gCtXFBIPPzy8JMhay4NWRg/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
|
|
@ -4280,6 +4334,7 @@
|
|||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
|
|
@ -6124,7 +6179,6 @@
|
|||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
|
|
@ -6636,6 +6690,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
|
|
@ -6719,6 +6774,7 @@
|
|||
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
|
|
@ -6870,6 +6926,12 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
|
||||
|
|
@ -7668,6 +7730,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@csstools/css-parser-algorithms": "^3.0.5",
|
||||
"@csstools/css-syntax-patches-for-csstree": "^1.0.19",
|
||||
|
|
@ -8082,6 +8145,7 @@
|
|||
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.15.0",
|
||||
|
|
@ -8406,6 +8470,7 @@
|
|||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
|
|
@ -8793,6 +8858,7 @@
|
|||
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -43,7 +43,9 @@
|
|||
"source-map": "^0.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"pocketbase": "^0.26.5",
|
||||
"dashjs": "^5.1.1"
|
||||
"butterchurn": "^2.6.7",
|
||||
"butterchurn-presets": "^2.4.7",
|
||||
"dashjs": "^5.1.1",
|
||||
"pocketbase": "^0.26.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue