*/ public static function allowedKeys(): array { return [ 'version', 'theme', 'floatingLines', 'appearance', 'accentColor', 'language', 'emotionGradientStart', 'emotionGradientEnd', 'timelineZoom', 'timelineScrollLeft', 'showFps', 'presets', 'activePresetId', ]; } /** * Keys allowed inside `floatingLines`, mirroring FLOATING_LINES_DEFAULTS * in the Quasar settings store. * * @return array */ public static function allowedFloatingLineKeys(): array { return [ // Lines 'speed', 'lineCount', 'spread', 'fanSpread', 'lineSharpness', 'lineThickness', 'waveFrequency', 'bezierCurvature', 'circleRadius', 'glowSize', 'glowStrength', 'lineBrightness', // Background 'bgCenter', 'bgEdge', 'gradientStops', 'backgroundImage', // Horizon 'horizonMode', 'horizonOpacity', 'horizonBlend', // Labels 'labelSize', 'labelColor', 'labelOpacity', 'labelConnectorLength', // Dot border 'dotBorderWidth', 'dotBorderColor', // Mode 'lineMode', 'staticLineColor', 'staticLineShadowStrength', ]; } /** * Validation rules for the settings document. * * @return array> */ public static function rules(): array { return [ 'settings' => ['required', 'array'], 'settings.version' => ['nullable', 'integer', 'min:1', 'max:'.self::VERSION], ...self::snapshotRules('settings.'), 'settings.timelineScrollLeft' => ['nullable', 'numeric', 'min:0'], 'settings.presets' => ['nullable', 'array', 'max:'.self::MAX_PRESETS], 'settings.presets.*' => ['array'], 'settings.presets.*.id' => ['required', 'string', 'max:100'], 'settings.presets.*.name' => ['required', 'string', 'max:100'], 'settings.presets.*.updatedAt' => ['nullable', 'numeric'], 'settings.presets.*.settings' => ['required', 'array'], ...self::snapshotRules('settings.presets.*.settings.'), 'settings.activePresetId' => ['nullable', 'string', 'max:100'], ]; } /** * Rules for the fields a preset snapshot shares with the top-level * settings. Every field needs an explicit rule: Laravel's `validated()` * only returns keys that have one, so a field left out here would be * silently dropped when a preset is saved. * * @return array> */ private static function snapshotRules(string $prefix): array { return [ $prefix.'theme' => ['nullable', 'string', 'max:50'], $prefix.'appearance' => ['nullable', 'string', 'max:50'], $prefix.'accentColor' => ['nullable', 'string', 'max:50'], $prefix.'language' => ['nullable', 'string', 'max:10'], $prefix.'emotionGradientStart' => ['nullable', 'string', 'max:20'], $prefix.'emotionGradientEnd' => ['nullable', 'string', 'max:20'], $prefix.'timelineZoom' => ['nullable', 'numeric', 'min:0.1', 'max:10'], $prefix.'showFps' => ['nullable', 'boolean'], $prefix.'floatingLines' => ['nullable', 'array'], $prefix.'floatingLines.*' => ['nullable', new ScalarSetting(self::MAX_STRING_LENGTH)], ]; } }