Merge pull request #282 from php-flasher/dev

update docs home page
This commit is contained in:
Younes ENNAJI
2025-03-30 02:33:13 +00:00
committed by GitHub
65 changed files with 3956 additions and 1804 deletions
@@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DashboardController extends Controller
{
/**
* Show the dashboard with overview of PHPFlasher features
*/
public function index()
{
flash()->option('timeout', 10000)
->info('Welcome to the PHPFlasher Laravel Demo! 👋 Explore different examples using the navigation.');
return view('dashboard');
}
/**
* Show all notification types at once
*/
public function showAllTypes()
{
flash()->success('This is a success notification!');
flash()->info('This is an information notification!');
flash()->warning('This is a warning notification!');
flash()->error('This is an error notification!');
return redirect()->back();
}
/**
* Show theme selector
*/
public function themeSelector()
{
return view('features.themes');
}
/**
* Display a notification with the selected theme
*/
public function showTheme(Request $request)
{
$theme = $request->input('theme', 'default');
flash()
->use("theme.$theme")
->success("This notification uses the '$theme' theme!");
return redirect()->route('themes');
}
}
@@ -0,0 +1,99 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class ExampleController extends Controller
{
/**
* Form validation example
*/
public function formExample()
{
return view('examples.form');
}
/**
* Process form submission
*/
public function processForm(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|min:2|max:50',
'email' => 'required|email',
'subject' => 'required|min:5',
'message' => 'required|min:10',
]);
if ($validator->fails()) {
flash()->error('Please fix the errors in the form!');
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Success scenario
flash()
->success('Your message has been sent successfully!')
->option('timeout', 8000);
return redirect()->route('form.example');
}
/**
* AJAX example page
*/
public function ajaxExample()
{
return view('examples.ajax');
}
/**
* Process AJAX request
*/
public function processAjax(Request $request)
{
$action = $request->input('action');
$success = rand(0, 10) > 3; // 70% success rate
if ($success) {
flash()->success("The $action action was completed successfully!");
return response()->json(['status' => 'success']);
} else {
flash()->error("The $action action failed. Please try again.");
return response()->json(['status' => 'error'], 422);
}
}
/**
* Demonstrate different notification positions
*/
public function positionsExample()
{
return view('examples.positions');
}
/**
* Show notification at specified position
*/
public function showAtPosition(Request $request)
{
$position = $request->input('position', 'top-right');
flash()
->option('position', $position)
->info("This notification appears at the '$position' position!");
return redirect()->route('positions.example');
}
/**
* Interactive playground to test notification options
*/
public function playground()
{
return view('examples.playground');
}
}
+11 -11
View File
@@ -16,25 +16,25 @@
"require": {
"php": "^8.2",
"inertiajs/inertia-laravel": "^2.0.1",
"laravel/framework": "^11.43.2",
"laravel/framework": "^12.3",
"laravel/tinker": "^2.10.1",
"livewire/livewire": "^3.5.20",
"livewire/livewire": "^3.6.2",
"php-flasher/php-flasher": "@dev",
"spatie/laravel-csp": "^2.10.3",
"spatie/laravel-ray": "^1.39.1"
"spatie/laravel-ray": "^1.40.2"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-debugbar": "^3.15.2",
"fakerphp/faker": "^1.24.1",
"larastan/larastan": "^3.1",
"laravel/pint": "^1.21",
"larastan/larastan": "^3.2",
"laravel/pint": "^1.21.2",
"laravel/sail": "^1.41",
"mockery/mockery": "^1.6.12",
"nunomaduro/collision": "^8.6.1",
"nunomaduro/collision": "^8.7.0",
"pestphp/pest": "^3.7.4",
"pestphp/pest-plugin-laravel": "^3.1",
"spatie/laravel-ignition": "^2.9.1",
"spatie/ray": "^1.41.5"
"spatie/ray": "^1.41.6"
},
"autoload": {
"psr-4": {
@@ -52,15 +52,15 @@
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi",
"@php artisan flasher:install --symlink"
"@php artisan flasher:install"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
"@php artisan flasher:install --symlink"
"@php artisan flasher:install"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
"@php artisan flasher:install --symlink"
"@php artisan flasher:install"
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi",
+351 -407
View File
File diff suppressed because it is too large Load Diff
+58 -8
View File
@@ -1,14 +1,64 @@
<?php
return [
declare(strict_types=1);
use Flasher\Prime\Configuration;
/*
* Default PHPFlasher configuration for Laravel.
*
* This configuration file defines the default settings for PHPFlasher when
* used within a Laravel application. It uses the Configuration class from
* the core PHPFlasher library to establish type-safe configuration.
*
* @return array<string, mixed> PHPFlasher configuration
*/
return Configuration::from([
// Default notification library (e.g., 'flasher', 'toastr', 'noty', 'notyf', 'sweetalert')
'default' => 'flasher',
// Path to the main PHPFlasher JavaScript file
'main_script' => '/vendor/flasher/flasher.min.js',
// List of CSS files to style your notifications
'styles' => [
'/vendor/flasher/flasher.min.css',
],
// Set global options for all notifications (optional)
// 'options' => [
// 'timeout' => 5000, // Time in milliseconds before the notification disappears
// 'position' => 'top-right', // Where the notification appears on the screen
// ],
// Automatically inject JavaScript and CSS assets into your HTML pages
'inject_assets' => true,
'options' => [
'timeout' => 5000, // in milliseconds
'position' => 'top-right',
// Enable message translation using Laravel's translation service
'translate' => true,
// URL patterns to exclude from asset injection and flash_bag conversion
'excluded_paths' => [],
// Map Laravel flash message keys to notification types
'flash_bag' => [
'success' => ['success'],
'error' => ['error', 'danger'],
'warning' => ['warning', 'alarm'],
'info' => ['info', 'notice', 'alert'],
],
'flash_bag' => [
'success' => ['success', 'ok', 'completed', 'passed', 'achieved'],
],
];
// Set criteria to filter which notifications are displayed (optional)
// 'filter' => [
// 'limit' => 5, // Maximum number of notifications to show at once
// ],
// Define notification presets to simplify notification creation (optional)
// 'presets' => [
// 'entity_saved' => [
// 'type' => 'success',
// 'title' => 'Entity saved',
// 'message' => 'Entity saved successfully',
// ],
// ],
]);
@@ -0,0 +1,83 @@
@extends('layouts.app')
@section('title', 'PHPFlasher Laravel Demo')
@section('content')
<div class="max-w-7xl mx-auto">
<div class="bg-white rounded-xl shadow-md overflow-hidden">
<div class="p-8">
<div class="uppercase tracking-wide text-sm text-indigo-600 font-semibold">Laravel Demo</div>
<h2 class="mt-2 text-3xl font-extrabold tracking-tight text-gray-900">Welcome to PHPFlasher</h2>
<p class="mt-4 text-lg text-gray-500">
PHPFlasher is a powerful notification library for PHP applications.
This demo shows how to use PHPFlasher in a Laravel project.
</p>
<div class="mt-8 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
<!-- Feature Cards -->
<div class="bg-indigo-50 p-6 rounded-lg">
<div class="text-indigo-600 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<h3 class="font-bold text-lg mb-2">Easy Integration</h3>
<p class="text-gray-600">Simple API to create notifications from anywhere in your Laravel application.</p>
</div>
<div class="bg-emerald-50 p-6 rounded-lg">
<div class="text-emerald-600 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
</svg>
</div>
<h3 class="font-bold text-lg mb-2">Multiple Themes</h3>
<p class="text-gray-600">Choose from 15+ beautiful themes or create your own custom themes.</p>
</div>
<div class="bg-amber-50 p-6 rounded-lg">
<div class="text-amber-600 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z" />
</svg>
</div>
<h3 class="font-bold text-lg mb-2">Flexible Positioning</h3>
<p class="text-gray-600">Display notifications at any corner or side of your screen.</p>
</div>
</div>
<div class="mt-10">
<h3 class="text-lg font-medium text-gray-900">Try It Now</h3>
<div class="mt-4 flex flex-wrap gap-2">
<a href="{{ route('all.types') }}" class="btn-primary">
Show All Notification Types
</a>
<a href="{{ route('themes') }}" class="btn-outline">
Explore Themes
</a>
</div>
</div>
<div class="mt-10">
<h3 class="text-lg font-medium text-gray-900">Code Example</h3>
<div class="mt-4 code-block">
<div class="code-header">
<span>Basic Usage</span>
</div>
<pre><code class="language-php">// Display a success notification
flash()->success('Item created successfully!');
// Display an error notification
flash()->error('An error occurred!');
// Display a warning notification
flash()->warning('Warning: This action cannot be undone.');
// Display an info notification
flash()->info('The task is running in the background.');</code></pre>
</div>
</div>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,67 @@
@extends('layouts.app')
@section('title', 'Form Example - PHPFlasher Laravel Demo')
@section('content')
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-xl shadow-md overflow-hidden">
<div class="p-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Contact Form Example</h2>
<div class="mb-8">
<p class="text-gray-600">
This example demonstrates how PHPFlasher can be used to display form validation errors and success messages.
</p>
</div>
<div class="code-block mb-8">
<div class="code-header">
<span>Controller Code</span>
</div>
<pre><code class="language-php">public function processForm(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|min:2|max:50',
'email' => 'required|email',
'subject' => 'required|min:5',
'message' => 'required|min:10',
]);
if ($validator->fails()) {
flash()->error('Please fix the errors in the form!');
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Success scenario
flash()
->success('Your message has been sent successfully!')
->option('timeout', 8000);
return redirect()->route('form.example');
}</code></pre>
</div>
<form action="{{ route('form.process') }}" method="POST" class="space-y-6">
@csrf
<div>
<label for="name" class="block text-sm font-medium text-gray-700">Name</label>
<input type="text" name="name" id="name" value="{{ old('name') }}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
@error('name')
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input type="email" name="email" id="email" value="{{ old('email') }}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
@error('email')
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label for="subject" class="block text-sm font-medium text-gray-700">Subject</label>
<input type="text" name="subject" id="subject" value="{{ old('subject
+224 -238
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -10,7 +10,7 @@ return [
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Nelmio\SecurityBundle\NelmioSecurityBundle::class => ['all' => true],
// Nelmio\SecurityBundle\NelmioSecurityBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
+8 -94
View File
@@ -1,7 +1,7 @@
flasher:
# Default notification library (e.g., 'flasher', 'toastr', 'noty', etc.)
# themes: flasher, crystal, emerald, sapphire
default: theme.flasher
default: theme.minimal
# Path to the main JavaScript file of PHPFlasher
main_script: '/vendor/flasher/flasher.min.js'
@@ -34,99 +34,13 @@ flasher:
# Limit number of displayed notifications
# limit: 5
plugins:
theme.amazon:
scripts: ['/vendor/flasher/themes/amazon/amazon.min.js']
themes:
amazon:
scripts:
- '/vendor/flasher/themes/amazon/amazon.min.js'
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/amazon/amazon.min.css'
theme.amber:
scripts: ['/vendor/flasher/themes/amber/amber.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/amber/amber.min.css'
theme.jade:
scripts: ['/vendor/flasher/themes/jade/jade.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/jade/jade.min.css'
theme.crystal:
scripts: ['/vendor/flasher/themes/crystal/crystal.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/crystal/crystal.min.css'
theme.emerald:
scripts: ['/vendor/flasher/themes/emerald/emerald.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/emerald/emerald.min.css'
theme.sapphire:
scripts: ['/vendor/flasher/themes/sapphire/sapphire.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/sapphire/sapphire.min.css'
theme.ruby:
scripts: ['/vendor/flasher/themes/ruby/ruby.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/ruby/ruby.min.css'
theme.onyx:
scripts: ['/vendor/flasher/themes/onyx/onyx.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/onyx/onyx.min.css'
theme.neon:
scripts: ['/vendor/flasher/themes/neon/neon.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/neon/neon.min.css'
theme.aurora:
scripts: ['/vendor/flasher/themes/aurora/aurora.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/aurora/aurora.min.css'
theme.minimal:
scripts: ['/vendor/flasher/themes/minimal/minimal.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/minimal/minimal.min.css'
theme.material:
scripts: ['/vendor/flasher/themes/material/material.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/material/material.min.css'
theme.google:
scripts: ['/vendor/flasher/themes/google/google.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/google/google.min.css'
theme.ios:
scripts: ['/vendor/flasher/themes/ios/ios.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/ios/ios.min.css'
theme.slack:
scripts: ['/vendor/flasher/themes/slack/slack.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/slack/slack.min.css'
theme.facebook:
scripts: ['/vendor/flasher/themes/facebook/facebook.min.js']
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/facebook/facebook.min.css'
options:
position: 'bottom-right'
timeout: 6000
@@ -1,33 +1,33 @@
nelmio_security:
# prevents framing of the entire site
clickjacking:
paths:
'^/.*': DENY
# disables content type sniffing for script resources
content_type:
nosniff: true
# forces Microsoft's XSS-Protection with
# its block mode
xss_protection:
enabled: true
mode_block: true
# Send a full URL in the `Referer` header when performing a same-origin request,
# only send the origin of the document to secure destination (HTTPS->HTTPS),
# and send no header to a less secure destination (HTTPS->HTTP).
# If `strict-origin-when-cross-origin` is not supported, use `no-referrer` policy,
# no referrer information is sent along with requests.
referrer_policy:
enabled: true
policies:
- 'no-referrer'
- 'strict-origin-when-cross-origin'
csp:
enabled: true
enforce:
script-src:
- 'self'
# nelmio_security:
# # prevents framing of the entire site
# clickjacking:
# paths:
# '^/.*': DENY
#
# # disables content type sniffing for script resources
# content_type:
# nosniff: true
#
# # forces Microsoft's XSS-Protection with
# # its block mode
# xss_protection:
# enabled: true
# mode_block: true
#
# # Send a full URL in the `Referer` header when performing a same-origin request,
# # only send the origin of the document to secure destination (HTTPS->HTTPS),
# # and send no header to a less secure destination (HTTPS->HTTP).
# # If `strict-origin-when-cross-origin` is not supported, use `no-referrer` policy,
# # no referrer information is sent along with requests.
# referrer_policy:
# enabled: true
# policies:
# - 'no-referrer'
# - 'strict-origin-when-cross-origin'
#
# csp:
# enabled: true
#
# enforce:
# script-src:
# - 'self'
@@ -0,0 +1,22 @@
document.addEventListener('DOMContentLoaded', function() {
const demoForm = document.getElementById('notification-demo-form');
if (demoForm) {
demoForm.addEventListener('submit', function(e) {
e.preventDefault();
const type = document.getElementById('notification-type').value;
const title = document.getElementById('notification-title').value;
const message = document.getElementById('notification-message').value;
const position = document.getElementById('notification-position').value;
const timeout = document.getElementById('notification-timeout').value;
// Use the PHPFlasher JavaScript API to show the notification
window.flasher[type](message, {
title: title,
position: position,
timeout: parseInt(timeout, 10)
});
});
}
});
+1 -1
View File
@@ -1 +1 @@
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("noty")):"function"==typeof define&&define.amd?define(["@flasher/flasher","noty"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).Noty=t(e.flasher,e.Noty)}(this,(function(e,t){"use strict";class s{success(e,t,s){this.flash("success",e,t,s)}error(e,t,s){this.flash("error",e,t,s)}info(e,t,s){this.flash("info",e,t,s)}warning(e,t,s){this.flash("warning",e,t,s)}flash(e,t,s,o){let i,n,r,a={};if("object"==typeof e?(a=Object.assign({},e),i=a.type,n=a.message,r=a.title,delete a.type,delete a.message,delete a.title):"object"==typeof t?(a=Object.assign({},t),i=e,n=a.message,r=a.title,delete a.message,delete a.title):"object"==typeof s?(a=Object.assign({},s),i=e,n=t,r=a.title,delete a.title):(i=e,n=t,r=s,a=o||{}),!i)throw new Error("Type is required for notifications");if(null==n)throw new Error("Message is required for notifications");const l={type:i,message:n,title:r||i,options:a,metadata:{plugin:""}};this.renderOptions(a),this.renderEnvelopes([l])}}const o=new class extends s{constructor(){super(...arguments),this.defaultOptions={timeout:1e4}}renderEnvelopes(e){(null==e?void 0:e.length)&&e.forEach((e=>{try{const s=Object.assign({text:e.message,type:e.type},this.defaultOptions);e.options&&Object.assign(s,e.options);const o=new t(s);o.show();const i=o.layoutDom;i&&"object"==typeof i.dataset&&(i.dataset.turboTemporary="")}catch(t){console.error("PHPFlasher Noty: Error rendering notification",t,e)}}))}renderOptions(e){e&&(Object.assign(this.defaultOptions,e),t.overrideDefaults(this.defaultOptions))}};return e.addPlugin("noty",o),o}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("noty")):"function"==typeof define&&define.amd?define(["@flasher/flasher","noty"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).Noty=t(e.flasher,e.Noty)}(this,(function(e,t){"use strict";class s{success(e,t,s){this.flash("success",e,t,s)}error(e,t,s){this.flash("error",e,t,s)}info(e,t,s){this.flash("info",e,t,s)}warning(e,t,s){this.flash("warning",e,t,s)}flash(e,t,s,o){let i,n,r,l={};if("object"==typeof e?(l=Object.assign({},e),i=l.type,n=l.message,r=l.title,delete l.type,delete l.message,delete l.title):"object"==typeof t?(l=Object.assign({},t),i=e,n=l.message,r=l.title,delete l.message,delete l.title):(i=e,n=t,null==s?(r=void 0,l=o||{}):"string"==typeof s?(r=s,l=o||{}):"object"==typeof s&&(l=Object.assign({},s),"title"in l?(r=l.title,delete l.title):r=void 0,o&&"object"==typeof o&&(l=Object.assign(Object.assign({},l),o)))),!i)throw new Error("Type is required for notifications");if(null==n)throw new Error("Message is required for notifications");null==r&&(r=i.charAt(0).toUpperCase()+i.slice(1));const a={type:i,message:n,title:r,options:l,metadata:{plugin:""}};this.renderOptions({}),this.renderEnvelopes([a])}}const o=new class extends s{constructor(){super(...arguments),this.defaultOptions={timeout:1e4}}renderEnvelopes(e){(null==e?void 0:e.length)&&e.forEach((e=>{try{const s=Object.assign({text:e.message,type:e.type},this.defaultOptions);e.options&&Object.assign(s,e.options);const o=new t(s);o.show();const i=o.layoutDom;i&&"object"==typeof i.dataset&&(i.dataset.turboTemporary="")}catch(t){console.error("PHPFlasher Noty: Error rendering notification",t,e)}}))}renderOptions(e){e&&(Object.assign(this.defaultOptions,e),t.overrideDefaults(this.defaultOptions))}};return e.addPlugin("noty",o),o}));
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("sweetalert2")):"function"==typeof define&&define.amd?define(["@flasher/flasher","sweetalert2"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).sweetalert=t(e.flasher,e.Swal)}(this,(function(e,t){"use strict";function r(e,t,r,s){return new(r||(r=Promise))((function(i,n){function o(e){try{a(s.next(e))}catch(e){n(e)}}function l(e){try{a(s.throw(e))}catch(e){n(e)}}function a(e){var t;e.done?i(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(o,l)}a((s=s.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class s{success(e,t,r){this.flash("success",e,t,r)}error(e,t,r){this.flash("error",e,t,r)}info(e,t,r){this.flash("info",e,t,r)}warning(e,t,r){this.flash("warning",e,t,r)}flash(e,t,r,s){let i,n,o,l={};if("object"==typeof e?(l=Object.assign({},e),i=l.type,n=l.message,o=l.title,delete l.type,delete l.message,delete l.title):"object"==typeof t?(l=Object.assign({},t),i=e,n=l.message,o=l.title,delete l.message,delete l.title):"object"==typeof r?(l=Object.assign({},r),i=e,n=t,o=l.title,delete l.title):(i=e,n=t,o=r,l=s||{}),!i)throw new Error("Type is required for notifications");if(null==n)throw new Error("Message is required for notifications");const a={type:i,message:n,title:o||i,options:l,metadata:{plugin:""}};this.renderOptions(l),this.renderEnvelopes([a])}}const i=new class extends s{renderEnvelopes(e){return r(this,void 0,void 0,(function*(){this.sweetalert||this.initializeSweetAlert();try{for(const t of e)yield this.renderEnvelope(t)}catch(e){console.error("PHPFlasher SweetAlert: Error rendering envelopes",e)}}))}renderOptions(e){try{this.sweetalert=this.sweetalert||t.mixin(Object.assign({timer:e.timer||1e4,timerProgressBar:e.timerProgressBar||!0},e)),this.setupTurboCompatibility()}catch(e){console.error("PHPFlasher SweetAlert: Error applying options",e)}}renderEnvelope(e){return r(this,void 0,void 0,(function*(){var t;try{let{options:r}=e;r=Object.assign(Object.assign({},r),{icon:(null==r?void 0:r.icon)||e.type,text:(null==r?void 0:r.text)||e.message});const s=yield null===(t=this.sweetalert)||void 0===t?void 0:t.fire(r);this.dispatchResultEvent(s,e)}catch(t){console.error("PHPFlasher SweetAlert: Error rendering envelope",t,e)}}))}initializeSweetAlert(){this.sweetalert||this.renderOptions({timer:1e4,timerProgressBar:!0})}setupTurboCompatibility(){document.addEventListener("turbo:before-cache",(()=>{if(t.isVisible()){const e=t.getPopup();e&&e.style.setProperty("animation-duration","0ms"),t.close()}}))}dispatchResultEvent(e,t){e&&window.dispatchEvent(new CustomEvent("flasher:sweetalert:promise",{detail:{promise:e,envelope:t}}))}};return e.addPlugin("sweetalert",i),i}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("sweetalert2")):"function"==typeof define&&define.amd?define(["@flasher/flasher","sweetalert2"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).sweetalert=t(e.flasher,e.Swal)}(this,(function(e,t){"use strict";function r(e,t,r,s){return new(r||(r=Promise))((function(i,n){function o(e){try{a(s.next(e))}catch(e){n(e)}}function l(e){try{a(s.throw(e))}catch(e){n(e)}}function a(e){var t;e.done?i(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(o,l)}a((s=s.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class s{success(e,t,r){this.flash("success",e,t,r)}error(e,t,r){this.flash("error",e,t,r)}info(e,t,r){this.flash("info",e,t,r)}warning(e,t,r){this.flash("warning",e,t,r)}flash(e,t,r,s){let i,n,o,l={};if("object"==typeof e?(l=Object.assign({},e),i=l.type,n=l.message,o=l.title,delete l.type,delete l.message,delete l.title):"object"==typeof t?(l=Object.assign({},t),i=e,n=l.message,o=l.title,delete l.message,delete l.title):(i=e,n=t,null==r?(o=void 0,l=s||{}):"string"==typeof r?(o=r,l=s||{}):"object"==typeof r&&(l=Object.assign({},r),"title"in l?(o=l.title,delete l.title):o=void 0,s&&"object"==typeof s&&(l=Object.assign(Object.assign({},l),s)))),!i)throw new Error("Type is required for notifications");if(null==n)throw new Error("Message is required for notifications");null==o&&(o=i.charAt(0).toUpperCase()+i.slice(1));const a={type:i,message:n,title:o,options:l,metadata:{plugin:""}};this.renderOptions({}),this.renderEnvelopes([a])}}const i=new class extends s{renderEnvelopes(e){return r(this,void 0,void 0,(function*(){this.sweetalert||this.initializeSweetAlert();try{for(const t of e)yield this.renderEnvelope(t)}catch(e){console.error("PHPFlasher SweetAlert: Error rendering envelopes",e)}}))}renderOptions(e){try{this.sweetalert=this.sweetalert||t.mixin(Object.assign({timer:e.timer||1e4,timerProgressBar:e.timerProgressBar||!0},e)),this.setupTurboCompatibility()}catch(e){console.error("PHPFlasher SweetAlert: Error applying options",e)}}renderEnvelope(e){return r(this,void 0,void 0,(function*(){var t;try{let{options:r}=e;r=Object.assign(Object.assign({},r),{icon:(null==r?void 0:r.icon)||e.type,text:(null==r?void 0:r.text)||e.message});const s=yield null===(t=this.sweetalert)||void 0===t?void 0:t.fire(r);this.dispatchResultEvent(s,e)}catch(t){console.error("PHPFlasher SweetAlert: Error rendering envelope",t,e)}}))}initializeSweetAlert(){this.sweetalert||this.renderOptions({timer:1e4,timerProgressBar:!0})}setupTurboCompatibility(){document.addEventListener("turbo:before-cache",(()=>{if(t.isVisible()){const e=t.getPopup();e&&e.style.setProperty("animation-duration","0ms"),t.close()}}))}dispatchResultEvent(e,t){e&&window.dispatchEvent(new CustomEvent("flasher:sweetalert:promise",{detail:{promise:e,envelope:t}}))}};return e.addPlugin("sweetalert",i),i}));
+1 -1
View File
@@ -1 +1 @@
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("toastr")):"function"==typeof define&&define.amd?define(["@flasher/flasher","toastr"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).toastr=t(e.flasher,e.toastr)}(this,(function(e,t){"use strict";class r{success(e,t,r){this.flash("success",e,t,r)}error(e,t,r){this.flash("error",e,t,r)}info(e,t,r){this.flash("info",e,t,r)}warning(e,t,r){this.flash("warning",e,t,r)}flash(e,t,r,s){let o,i,n,a={};if("object"==typeof e?(a=Object.assign({},e),o=a.type,i=a.message,n=a.title,delete a.type,delete a.message,delete a.title):"object"==typeof t?(a=Object.assign({},t),o=e,i=a.message,n=a.title,delete a.message,delete a.title):"object"==typeof r?(a=Object.assign({},r),o=e,i=t,n=a.title,delete a.title):(o=e,i=t,n=r,a=s||{}),!o)throw new Error("Type is required for notifications");if(null==i)throw new Error("Message is required for notifications");const l={type:o,message:i,title:n||o,options:a,metadata:{plugin:""}};this.renderOptions(a),this.renderEnvelopes([l])}}const s=new class extends r{renderEnvelopes(e){(null==e?void 0:e.length)&&this.isDependencyAvailable()&&e.forEach((e=>{try{const{message:r,title:s,type:o,options:i}=e,n=t[o](r,s,i);if(n&&n.parent)try{const e=n.parent();e&&"function"==typeof e.attr&&e.attr("data-turbo-temporary","")}catch(e){console.error("PHPFlasher Toastr: Error setting Turbo compatibility",e)}}catch(t){console.error("PHPFlasher Toastr: Error rendering notification",t,e)}}))}renderOptions(e){if(this.isDependencyAvailable())try{t.options=Object.assign({timeOut:e.timeOut||1e4,progressBar:e.progressBar||!0},e)}catch(e){console.error("PHPFlasher Toastr: Error applying options",e)}}isDependencyAvailable(){return!(!window.jQuery&&!window.$)||(console.error("PHPFlasher Toastr: jQuery is required but not loaded. Make sure jQuery is loaded before using Toastr."),!1)}};return e.addPlugin("toastr",s),s}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("toastr")):"function"==typeof define&&define.amd?define(["@flasher/flasher","toastr"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).toastr=t(e.flasher,e.toastr)}(this,(function(e,t){"use strict";class s{success(e,t,s){this.flash("success",e,t,s)}error(e,t,s){this.flash("error",e,t,s)}info(e,t,s){this.flash("info",e,t,s)}warning(e,t,s){this.flash("warning",e,t,s)}flash(e,t,s,r){let o,i,n,a={};if("object"==typeof e?(a=Object.assign({},e),o=a.type,i=a.message,n=a.title,delete a.type,delete a.message,delete a.title):"object"==typeof t?(a=Object.assign({},t),o=e,i=a.message,n=a.title,delete a.message,delete a.title):(o=e,i=t,null==s?(n=void 0,a=r||{}):"string"==typeof s?(n=s,a=r||{}):"object"==typeof s&&(a=Object.assign({},s),"title"in a?(n=a.title,delete a.title):n=void 0,r&&"object"==typeof r&&(a=Object.assign(Object.assign({},a),r)))),!o)throw new Error("Type is required for notifications");if(null==i)throw new Error("Message is required for notifications");null==n&&(n=o.charAt(0).toUpperCase()+o.slice(1));const l={type:o,message:i,title:n,options:a,metadata:{plugin:""}};this.renderOptions({}),this.renderEnvelopes([l])}}const r=new class extends s{renderEnvelopes(e){(null==e?void 0:e.length)&&this.isDependencyAvailable()&&e.forEach((e=>{try{const{message:s,title:r,type:o,options:i}=e,n=t[o](s,r,i);if(n&&n.parent)try{const e=n.parent();e&&"function"==typeof e.attr&&e.attr("data-turbo-temporary","")}catch(e){console.error("PHPFlasher Toastr: Error setting Turbo compatibility",e)}}catch(t){console.error("PHPFlasher Toastr: Error rendering notification",t,e)}}))}renderOptions(e){if(this.isDependencyAvailable())try{t.options=Object.assign({timeOut:e.timeOut||1e4,progressBar:e.progressBar||!0},e)}catch(e){console.error("PHPFlasher Toastr: Error applying options",e)}}isDependencyAvailable(){return!(!window.jQuery&&!window.$)||(console.error("PHPFlasher Toastr: jQuery is required but not loaded. Make sure jQuery is loaded before using Toastr."),!1)}};return e.addPlugin("toastr",r),r}));
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+24 -24
View File
@@ -1,51 +1,51 @@
{
"/vendor/flasher/flasher.min.js": "/vendor/flasher/flasher.min.js?id=2d20fa21f78414571c3878794170542b",
"/vendor/flasher/flasher.min.css": "/vendor/flasher/flasher.min.css?id=2ec8e2ef9e675401b21bea5e5fc4869e",
"/vendor/flasher/themes/amazon/amazon.min.css": "/vendor/flasher/themes/amazon/amazon.min.css?id=9382a41bbc9522b14fa2ffb7df0f0568",
"/vendor/flasher/flasher.min.js": "/vendor/flasher/flasher.min.js?id=2dfca2b3c2159f2ff676a62ad338f092",
"/vendor/flasher/flasher.min.css": "/vendor/flasher/flasher.min.css?id=689710919ac658a64bacd98998f04534",
"/vendor/flasher/themes/amazon/amazon.min.css": "/vendor/flasher/themes/amazon/amazon.min.css?id=9ee70f25ed683a1a06c96832e53ae372",
"/vendor/flasher/themes/amazon/amazon.min.js": "/vendor/flasher/themes/amazon/amazon.min.js?id=2e8cbc1a93524a122b8f62f072f2ff71",
"/vendor/flasher/themes/sapphire/sapphire.min.css": "/vendor/flasher/themes/sapphire/sapphire.min.css?id=6ec8e39cb6b86690ed7b91d3e6e05249",
"/vendor/flasher/themes/sapphire/sapphire.min.css": "/vendor/flasher/themes/sapphire/sapphire.min.css?id=f7b4efc7b645ee792dacd4438fdc4ea3",
"/vendor/flasher/themes/sapphire/sapphire.min.js": "/vendor/flasher/themes/sapphire/sapphire.min.js?id=aeaeda5a1eadd052f0c466fdf6c6f7b3",
"/vendor/flasher/themes/google/google.min.js": "/vendor/flasher/themes/google/google.min.js?id=812cddda9e58967cd46fb1f5a385b8ec",
"/vendor/flasher/themes/google/google.min.css": "/vendor/flasher/themes/google/google.min.css?id=9f1ec427e902cc5cac013ec83b807009",
"/vendor/flasher/themes/google/google.min.css": "/vendor/flasher/themes/google/google.min.css?id=b5f06fb2a4795ce361e94113d3b91822",
"/vendor/flasher/themes/flasher/flasher.min.js": "/vendor/flasher/themes/flasher/flasher.min.js?id=7b56b75dafcf90e2fd3cf243f6e17ede",
"/vendor/flasher/themes/flasher/flasher.min.css": "/vendor/flasher/themes/flasher/flasher.min.css?id=967ef3aa022e0fe9325c45f95a2c613c",
"/vendor/flasher/themes/aurora/aurora.min.css": "/vendor/flasher/themes/aurora/aurora.min.css?id=ae3be7d52a558efcd528acd5953e7f9b",
"/vendor/flasher/themes/flasher/flasher.min.css": "/vendor/flasher/themes/flasher/flasher.min.css?id=a806450943da43e7876dd3ddf78c47bc",
"/vendor/flasher/themes/aurora/aurora.min.css": "/vendor/flasher/themes/aurora/aurora.min.css?id=c147c3dce846ce387a2360fb7c5ef317",
"/vendor/flasher/themes/aurora/aurora.min.js": "/vendor/flasher/themes/aurora/aurora.min.js?id=16817691e8c76e7a27ea1be3a9d3e288",
"/vendor/flasher/themes/crystal/crystal.min.css": "/vendor/flasher/themes/crystal/crystal.min.css?id=50f30960c17127b692157c79e8e2a6bf",
"/vendor/flasher/themes/crystal/crystal.min.css": "/vendor/flasher/themes/crystal/crystal.min.css?id=22d1ac60ba71b99d33f06d8860542bea",
"/vendor/flasher/themes/crystal/crystal.min.js": "/vendor/flasher/themes/crystal/crystal.min.js?id=b98d586c16df24f556bd509d128d4206",
"/vendor/flasher/themes/minimal/minimal.min.css": "/vendor/flasher/themes/minimal/minimal.min.css?id=e85909c765088fd771f79722f8044083",
"/vendor/flasher/themes/minimal/minimal.min.css": "/vendor/flasher/themes/minimal/minimal.min.css?id=ece6c8d8425a37426f2953e7d771cd34",
"/vendor/flasher/themes/minimal/minimal.min.js": "/vendor/flasher/themes/minimal/minimal.min.js?id=1497c70c9875d728c439136c92fbca08",
"/vendor/flasher/themes/ios/ios.min.js": "/vendor/flasher/themes/ios/ios.min.js?id=7149c00236bda6f7aecfd7f0ce18a1f4",
"/vendor/flasher/themes/ios/ios.min.css": "/vendor/flasher/themes/ios/ios.min.css?id=770dd6b169caba737bb7c815a4beb43c",
"/vendor/flasher/themes/onyx/onyx.min.css": "/vendor/flasher/themes/onyx/onyx.min.css?id=57bf33a47434641e70c12e4677cffe7a",
"/vendor/flasher/themes/ios/ios.min.css": "/vendor/flasher/themes/ios/ios.min.css?id=9680ce2ce92ac435c99a40e599d2e775",
"/vendor/flasher/themes/onyx/onyx.min.css": "/vendor/flasher/themes/onyx/onyx.min.css?id=eff8e7447767c39298a9c3565a917d79",
"/vendor/flasher/themes/onyx/onyx.min.js": "/vendor/flasher/themes/onyx/onyx.min.js?id=2c8dc59c4abefd370535eb102d4e6711",
"/vendor/flasher/themes/amber/amber.min.js": "/vendor/flasher/themes/amber/amber.min.js?id=95b57e413d5cb896b47ad9158dd4ef07",
"/vendor/flasher/themes/amber/amber.min.css": "/vendor/flasher/themes/amber/amber.min.css?id=e4db7add360531073703b31fcbb68f09",
"/vendor/flasher/themes/facebook/facebook.min.js": "/vendor/flasher/themes/facebook/facebook.min.js?id=3080bc5072f5b7379d281c2f98801f86",
"/vendor/flasher/themes/facebook/facebook.min.css": "/vendor/flasher/themes/facebook/facebook.min.css?id=de4ddab8b92e5004478621f08364ccf0",
"/vendor/flasher/themes/neon/neon.min.css": "/vendor/flasher/themes/neon/neon.min.css?id=0fa1e1aff230421aab943153f5154461",
"/vendor/flasher/themes/amber/amber.min.css": "/vendor/flasher/themes/amber/amber.min.css?id=20aa374b7ede682a6a0eb00c19a0013c",
"/vendor/flasher/themes/facebook/facebook.min.js": "/vendor/flasher/themes/facebook/facebook.min.js?id=7ce0a41e3886db70a56b6e9e49bdda91",
"/vendor/flasher/themes/facebook/facebook.min.css": "/vendor/flasher/themes/facebook/facebook.min.css?id=edfbad59ded6831073a0d08a799757fd",
"/vendor/flasher/themes/neon/neon.min.css": "/vendor/flasher/themes/neon/neon.min.css?id=38716f3f934f720ac34fb6617d5dc674",
"/vendor/flasher/themes/neon/neon.min.js": "/vendor/flasher/themes/neon/neon.min.js?id=01987a2d265f63b2e92fe5f3e4b61496",
"/vendor/flasher/themes/slack/slack.min.js": "/vendor/flasher/themes/slack/slack.min.js?id=6b1ade06f3eb351e9eaef76d402918fb",
"/vendor/flasher/themes/slack/slack.min.css": "/vendor/flasher/themes/slack/slack.min.css?id=6fcab491611c4726877f14842e409dea",
"/vendor/flasher/themes/slack/slack.min.css": "/vendor/flasher/themes/slack/slack.min.css?id=4fa1fe1bdca7c256f225e9f4c2e6d779",
"/vendor/flasher/themes/jade/jade.min.js": "/vendor/flasher/themes/jade/jade.min.js?id=0fbcab9da8649325269c934da21006cc",
"/vendor/flasher/themes/jade/jade.min.css": "/vendor/flasher/themes/jade/jade.min.css?id=7d665c1bb7db81e076254ec0ace02ea6",
"/vendor/flasher/themes/jade/jade.min.css": "/vendor/flasher/themes/jade/jade.min.css?id=b140ea3056bc6eaa860c891bedf16818",
"/vendor/flasher/themes/material/material.min.js": "/vendor/flasher/themes/material/material.min.js?id=0360045692aaa32af12dc9739374fdae",
"/vendor/flasher/themes/material/material.min.css": "/vendor/flasher/themes/material/material.min.css?id=463c4b306917d3ed573126aee949076d",
"/vendor/flasher/themes/ruby/ruby.min.css": "/vendor/flasher/themes/ruby/ruby.min.css?id=00a9a89e35cab2959a4d8bc34bb2f174",
"/vendor/flasher/themes/material/material.min.css": "/vendor/flasher/themes/material/material.min.css?id=f0327e5645332c10d957fff6af1220fc",
"/vendor/flasher/themes/ruby/ruby.min.css": "/vendor/flasher/themes/ruby/ruby.min.css?id=faffa15686ab77a5f8988d1b8cc54650",
"/vendor/flasher/themes/ruby/ruby.min.js": "/vendor/flasher/themes/ruby/ruby.min.js?id=7c8e19091329bd9242408f8d5b9890e7",
"/vendor/flasher/themes/emerald/emerald.min.css": "/vendor/flasher/themes/emerald/emerald.min.css?id=446415c8c2364cb291a2c6fe576532b2",
"/vendor/flasher/themes/emerald/emerald.min.css": "/vendor/flasher/themes/emerald/emerald.min.css?id=4e346793d433693b3fdfd9de46d05590",
"/vendor/flasher/themes/emerald/emerald.min.js": "/vendor/flasher/themes/emerald/emerald.min.js?id=815113a3e5023948b5f700f5d62253dd",
"/vendor/flasher/mint.css": "/vendor/flasher/mint.css?id=348f135fff639305dde0005c647c1d20",
"/vendor/flasher/flasher-noty.min.js": "/vendor/flasher/flasher-noty.min.js?id=745c58b03b20161745f05229c7e0ddad",
"/vendor/flasher/flasher-noty.min.js": "/vendor/flasher/flasher-noty.min.js?id=4a788f294fa818764ee4d3e1cf10a330",
"/vendor/flasher/noty.css": "/vendor/flasher/noty.css?id=bf51111a785e04cc8c86a7786e855484",
"/vendor/flasher/noty.min.js": "/vendor/flasher/noty.min.js?id=840a31ddb720ff391cfc386c009d3422",
"/vendor/flasher/flasher-notyf.min.js": "/vendor/flasher/flasher-notyf.min.js?id=1177c4554468e76e9877ec0fdc133e1a",
"/vendor/flasher/flasher-notyf.min.js": "/vendor/flasher/flasher-notyf.min.js?id=d6c390269f18e78bc9c74a98fc6c3c29",
"/vendor/flasher/flasher-notyf.min.css": "/vendor/flasher/flasher-notyf.min.css?id=a68d410be6df5aaa1dd8ed3a2798d390",
"/vendor/flasher/toastr.min.css": "/vendor/flasher/toastr.min.css?id=f284028c678041d687c6f1be6968f68a",
"/vendor/flasher/flasher-toastr.min.js": "/vendor/flasher/flasher-toastr.min.js?id=6e79f51a11a2b977b0e0eba8ee37c1d9",
"/vendor/flasher/flasher-toastr.min.js": "/vendor/flasher/flasher-toastr.min.js?id=1ba7dae06e6524905228794c1db14290",
"/vendor/flasher/toastr.min.js": "/vendor/flasher/toastr.min.js?id=8ee1218b09fb02d43fcf0b84e30637ad",
"/vendor/flasher/jquery.min.js": "/vendor/flasher/jquery.min.js?id=2c872dbe60f4ba70fb85356113d8b35e",
"/vendor/flasher/sweetalert2.min.js": "/vendor/flasher/sweetalert2.min.js?id=bba3afcfe7dcc339fa25fad49842c2bf",
"/vendor/flasher/sweetalert2.min.css": "/vendor/flasher/sweetalert2.min.css?id=83b54381abbed7676c22f411eba2c61d",
"/vendor/flasher/flasher-sweetalert.min.js": "/vendor/flasher/flasher-sweetalert.min.js?id=bb1df6a783dd98547ccc21338b1c7d5d"
"/vendor/flasher/flasher-sweetalert.min.js": "/vendor/flasher/flasher-sweetalert.min.js?id=471a0b1aec6d6adc8e42a7c5dfcf0a93"
}
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
.fl-amber{--amber-bg-light:#fff;--amber-bg-dark:#1e293b;--amber-text-light:#4b5563;--amber-text-dark:#f1f5f9;--amber-shadow:0 5px 15px rgba(0,0,0,.08);--amber-shadow-dark:0 5px 15px rgba(0,0,0,.2);--amber-border-radius:0.4rem;--amber-success:#10b981;--amber-info:#3b82f6;--amber-warning:#f59e0b;--amber-error:#ef4444}@keyframes amberIn{0%{opacity:0;transform:translateY(-12px)}to{opacity:1;transform:translateY(0)}}.fl-amber{animation:amberIn .3s ease-out;background-color:var(--amber-bg-light);border-radius:var(--amber-border-radius);box-shadow:var(--amber-shadow);color:var(--amber-text-light);font-family:var(--fl-font),serif;margin:.6rem 0;padding:.85rem 1rem;position:relative;will-change:transform,opacity}.fl-amber:last-child{margin-bottom:0}.fl-amber .fl-content{align-items:center;display:flex}.fl-amber .fl-icon{font-size:1.85em;margin-right:.8rem}.fl-amber .fl-text{flex:1}.fl-amber .fl-message{font-size:.875em;line-height:1.4}.fl-amber .fl-close{background:none;border:none;color:currentColor;cursor:pointer;flex-shrink:0;font-size:1.15rem;margin-left:1rem;opacity:.6;padding:.25rem;touch-action:manipulation;transition:opacity .2s}.fl-amber .fl-close:focus,.fl-amber .fl-close:hover{opacity:1}.fl-amber .fl-progress-bar{bottom:0;height:3px;left:0;overflow:hidden;position:absolute;right:0}.fl-amber .fl-progress-bar .fl-progress{height:100%;width:100%}.fl-amber.fl-success .fl-close{color:var(--amber-success)}.fl-amber.fl-info .fl-close{color:var(--amber-info)}.fl-amber.fl-warning .fl-close{color:var(--amber-warning)}.fl-amber.fl-error .fl-close{color:var(--amber-error)}.fl-amber.fl-rtl{direction:rtl}.fl-amber.fl-rtl .fl-icon{margin-left:.8rem;margin-right:0}.fl-amber.fl-rtl .fl-close{margin-left:0;margin-right:1rem}.fl-amber.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-amber{animation:none}}.fl-amber.fl-auto-dark,body.fl-dark .fl-amber,html.fl-dark .fl-amber{background-color:var(--amber-bg-dark);box-shadow:var(--amber-shadow-dark);color:var(--amber-text-dark)}
.fl-amber{--amber-bg-light:#fff;--amber-bg-dark:#1e293b;--amber-text-light:#4b5563;--amber-text-dark:#f1f5f9;--amber-shadow:0 5px 15px rgba(0,0,0,.08);--amber-shadow-dark:0 5px 15px rgba(0,0,0,.2);--amber-border-radius:4px;--amber-success:#10b981;--amber-info:#3b82f6;--amber-warning:#f59e0b;--amber-error:#ef4444}@keyframes amberIn{0%{opacity:0;transform:translateY(-12px)}to{opacity:1;transform:translateY(0)}}.fl-amber{animation:amberIn .3s ease-out;background-color:var(--amber-bg-light);border-radius:var(--amber-border-radius) var(--amber-border-radius) 0 0;box-shadow:var(--amber-shadow);color:var(--amber-text-light);font-family:var(--fl-font),serif;margin:.6rem 0;padding:.85rem 1rem;position:relative;will-change:transform,opacity}.fl-amber .fl-content{align-items:center;display:flex}.fl-amber .fl-icon{font-size:1.85em;margin-right:.8rem}.fl-amber .fl-text{flex:1}.fl-amber .fl-message{font-size:.875em;line-height:1.4}.fl-amber .fl-close{background:none;border:none;color:currentColor;cursor:pointer;flex-shrink:0;font-size:1.15rem;margin-left:1rem;opacity:.6;padding:.25rem;touch-action:manipulation;transition:opacity .2s}.fl-amber .fl-close:focus,.fl-amber .fl-close:hover{opacity:1}.fl-amber .fl-progress-bar{bottom:0;height:3px;left:0;overflow:hidden;position:absolute;right:0}.fl-amber .fl-progress-bar .fl-progress{height:100%;width:100%}.fl-amber.fl-success .fl-close{color:var(--amber-success)}.fl-amber.fl-info .fl-close{color:var(--amber-info)}.fl-amber.fl-warning .fl-close{color:var(--amber-warning)}.fl-amber.fl-error .fl-close{color:var(--amber-error)}.fl-amber.fl-rtl{direction:rtl}.fl-amber.fl-rtl .fl-icon{margin-left:.8rem;margin-right:0}.fl-amber.fl-rtl .fl-close{margin-left:0;margin-right:1rem}.fl-amber.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-amber{animation:none}}.fl-amber.fl-auto-dark,body.fl-dark .fl-amber,html.fl-dark .fl-amber{background-color:var(--amber-bg-dark);box-shadow:var(--amber-shadow-dark);color:var(--amber-text-dark)}
@@ -1 +1 @@
.fl-aurora{--aurora-bg-light:hsla(0,0%,100%,.95);--aurora-bg-dark:rgba(20,20,28,.92);--aurora-text-light:#1e293b;--aurora-text-dark:#f8fafc;--aurora-shadow:0 8px 25px rgba(0,0,0,.08);--aurora-shadow-dark:0 10px 30px rgba(0,0,0,.16);--aurora-border-radius:16px;--aurora-blur:15px;--aurora-success-gradient:linear-gradient(135deg,rgba(16,185,129,.08),rgba(16,185,129,.2));--aurora-info-gradient:linear-gradient(135deg,rgba(59,130,246,.08),rgba(59,130,246,.2));--aurora-warning-gradient:linear-gradient(135deg,rgba(245,158,11,.08),rgba(245,158,11,.2));--aurora-error-gradient:linear-gradient(135deg,rgba(239,68,68,.08),rgba(239,68,68,.2));--aurora-success:#10b981;--aurora-info:#3b82f6;--aurora-warning:#f59e0b;--aurora-error:#ef4444}@keyframes auroraFadeIn{0%{opacity:0;transform:translateY(-12px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.fl-aurora{animation:auroraFadeIn .35s cubic-bezier(.21,1.02,.73,1);backdrop-filter:blur(var(--aurora-blur));-webkit-backdrop-filter:blur(var(--aurora-blur));background-color:var(--aurora-bg-light);border-radius:var(--aurora-border-radius);box-shadow:var(--aurora-shadow);color:var(--aurora-text-light);font-family:var(--fl-font),sans-serif;margin:10px 0;overflow:hidden;padding:16px 18px;position:relative;will-change:transform,opacity}.fl-aurora:before{border-radius:inherit;content:"";inset:0;opacity:.8;position:absolute;z-index:0}.fl-aurora .fl-content{align-items:center;display:flex;position:relative;z-index:1}.fl-aurora .fl-message{flex:1;font-size:.9375rem;font-weight:500;line-height:1.5;margin-right:10px}.fl-aurora .fl-close{align-items:center;background:rgba(0,0,0,.05);border:none;border-radius:50%;color:inherit;cursor:pointer;display:flex;flex-shrink:0;font-size:1rem;height:28px;justify-content:center;opacity:.7;transition:all .2s ease;width:28px}.fl-aurora .fl-close:focus,.fl-aurora .fl-close:hover{background:rgba(0,0,0,.1);opacity:1}.fl-aurora .fl-progress-bar{border-radius:6px;bottom:2px;height:3px;left:2px;opacity:.7;overflow:hidden;position:absolute;right:2px;z-index:1}.fl-aurora .fl-progress{height:100%;width:100%}.fl-aurora.fl-success:before{background:var(--aurora-success-gradient)}.fl-aurora.fl-success .fl-progress{background-color:var(--aurora-success)}.fl-aurora.fl-info:before{background:var(--aurora-info-gradient)}.fl-aurora.fl-info .fl-progress{background-color:var(--aurora-info)}.fl-aurora.fl-warning:before{background:var(--aurora-warning-gradient)}.fl-aurora.fl-warning .fl-progress{background-color:var(--aurora-warning)}.fl-aurora.fl-error:before{background:var(--aurora-error-gradient)}.fl-aurora.fl-error .fl-progress{background-color:var(--aurora-error)}.fl-aurora.fl-rtl{direction:rtl}.fl-aurora.fl-rtl .fl-message{margin-left:10px;margin-right:0}.fl-aurora.fl-rtl .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-aurora{animation:none}}.fl-aurora.fl-auto-dark,body.fl-dark .fl-aurora,html.fl-dark .fl-aurora{background-color:var(--aurora-bg-dark);box-shadow:var(--aurora-shadow-dark);color:var(--aurora-text-dark)}.fl-aurora.fl-auto-dark .fl-close,body.fl-dark .fl-aurora .fl-close,html.fl-dark .fl-aurora .fl-close{background:hsla(0,0%,100%,.1)}.fl-aurora.fl-auto-dark .fl-close:focus,.fl-aurora.fl-auto-dark .fl-close:hover,body.fl-dark .fl-aurora .fl-close:focus,body.fl-dark .fl-aurora .fl-close:hover,html.fl-dark .fl-aurora .fl-close:focus,html.fl-dark .fl-aurora .fl-close:hover{background:hsla(0,0%,100%,.15)}.fl-aurora.fl-auto-dark.fl-success:before,body.fl-dark .fl-aurora.fl-success:before,html.fl-dark .fl-aurora.fl-success:before{background:linear-gradient(135deg,rgba(16,185,129,.1),rgba(16,185,129,.25))}.fl-aurora.fl-auto-dark.fl-info:before,body.fl-dark .fl-aurora.fl-info:before,html.fl-dark .fl-aurora.fl-info:before{background:linear-gradient(135deg,rgba(59,130,246,.1),rgba(59,130,246,.25))}.fl-aurora.fl-auto-dark.fl-warning:before,body.fl-dark .fl-aurora.fl-warning:before,html.fl-dark .fl-aurora.fl-warning:before{background:linear-gradient(135deg,rgba(245,158,11,.1),rgba(245,158,11,.25))}.fl-aurora.fl-auto-dark.fl-error:before,body.fl-dark .fl-aurora.fl-error:before,html.fl-dark .fl-aurora.fl-error:before{background:linear-gradient(135deg,rgba(239,68,68,.1),rgba(239,68,68,.25))}
.fl-aurora{--aurora-bg-light:#fff;--aurora-bg-dark:#14141c;--aurora-text-light:#1e293b;--aurora-text-dark:#f8fafc;--aurora-shadow:0 8px 25px rgba(0,0,0,.08);--aurora-shadow-dark:0 10px 30px rgba(0,0,0,.16);--aurora-border-radius:4px;--aurora-blur:15px;--aurora-success-gradient:linear-gradient(135deg,rgba(16,185,129,.08),rgba(16,185,129,.2));--aurora-info-gradient:linear-gradient(135deg,rgba(59,130,246,.08),rgba(59,130,246,.2));--aurora-warning-gradient:linear-gradient(135deg,rgba(245,158,11,.08),rgba(245,158,11,.2));--aurora-error-gradient:linear-gradient(135deg,rgba(239,68,68,.08),rgba(239,68,68,.2));--aurora-success:#10b981;--aurora-info:#3b82f6;--aurora-warning:#f59e0b;--aurora-error:#ef4444}@keyframes auroraFadeIn{0%{opacity:0;transform:translateY(-12px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.fl-aurora{animation:auroraFadeIn .35s cubic-bezier(.21,1.02,.73,1);backdrop-filter:blur(var(--aurora-blur));-webkit-backdrop-filter:blur(var(--aurora-blur));background-color:var(--aurora-bg-light);border-radius:var(--aurora-border-radius) var(--aurora-border-radius) 0 0;box-shadow:var(--aurora-shadow);color:var(--aurora-text-light);font-family:var(--fl-font),sans-serif;margin:10px 0;overflow:hidden;padding:16px 18px;position:relative;will-change:transform,opacity}.fl-aurora:before{border-radius:inherit;content:"";inset:0;opacity:.8;position:absolute;z-index:0}.fl-aurora .fl-content{align-items:center;display:flex;position:relative;z-index:1}.fl-aurora .fl-message{flex:1;font-size:.9375rem;font-weight:500;line-height:1.5;margin-right:10px}.fl-aurora .fl-close{align-items:center;background:rgba(0,0,0,.05);border:none;border-radius:50%;color:inherit;cursor:pointer;display:flex;flex-shrink:0;font-size:1rem;height:28px;justify-content:center;opacity:.7;transition:all .2s ease;width:28px}.fl-aurora .fl-close:focus,.fl-aurora .fl-close:hover{background:rgba(0,0,0,.1);opacity:1}.fl-aurora .fl-progress-bar{border-radius:6px;bottom:2px;height:3px;left:2px;opacity:.7;overflow:hidden;position:absolute;right:2px;z-index:1}.fl-aurora .fl-progress{height:100%;width:100%}.fl-aurora.fl-success:before{background:var(--aurora-success-gradient)}.fl-aurora.fl-success .fl-progress{background-color:var(--aurora-success)}.fl-aurora.fl-info:before{background:var(--aurora-info-gradient)}.fl-aurora.fl-info .fl-progress{background-color:var(--aurora-info)}.fl-aurora.fl-warning:before{background:var(--aurora-warning-gradient)}.fl-aurora.fl-warning .fl-progress{background-color:var(--aurora-warning)}.fl-aurora.fl-error:before{background:var(--aurora-error-gradient)}.fl-aurora.fl-error .fl-progress{background-color:var(--aurora-error)}.fl-aurora.fl-rtl{direction:rtl}.fl-aurora.fl-rtl .fl-message{margin-left:10px;margin-right:0}.fl-aurora.fl-rtl .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-aurora{animation:none}}.fl-aurora.fl-auto-dark,body.fl-dark .fl-aurora,html.fl-dark .fl-aurora{background-color:var(--aurora-bg-dark);box-shadow:var(--aurora-shadow-dark);color:var(--aurora-text-dark)}.fl-aurora.fl-auto-dark .fl-close,body.fl-dark .fl-aurora .fl-close,html.fl-dark .fl-aurora .fl-close{background:hsla(0,0%,100%,.1)}.fl-aurora.fl-auto-dark .fl-close:focus,.fl-aurora.fl-auto-dark .fl-close:hover,body.fl-dark .fl-aurora .fl-close:focus,body.fl-dark .fl-aurora .fl-close:hover,html.fl-dark .fl-aurora .fl-close:focus,html.fl-dark .fl-aurora .fl-close:hover{background:hsla(0,0%,100%,.15)}.fl-aurora.fl-auto-dark.fl-success:before,body.fl-dark .fl-aurora.fl-success:before,html.fl-dark .fl-aurora.fl-success:before{background:linear-gradient(135deg,rgba(16,185,129,.1),rgba(16,185,129,.25))}.fl-aurora.fl-auto-dark.fl-info:before,body.fl-dark .fl-aurora.fl-info:before,html.fl-dark .fl-aurora.fl-info:before{background:linear-gradient(135deg,rgba(59,130,246,.1),rgba(59,130,246,.25))}.fl-aurora.fl-auto-dark.fl-warning:before,body.fl-dark .fl-aurora.fl-warning:before,html.fl-dark .fl-aurora.fl-warning:before{background:linear-gradient(135deg,rgba(245,158,11,.1),rgba(245,158,11,.25))}.fl-aurora.fl-auto-dark.fl-error:before,body.fl-dark .fl-aurora.fl-error:before,html.fl-dark .fl-aurora.fl-error:before{background:linear-gradient(135deg,rgba(239,68,68,.1),rgba(239,68,68,.25))}
@@ -1 +1 @@
.fl-crystal{--crystal-bg-light:#fff;--crystal-bg-dark:rgba(30,30,30,.95);--crystal-text-light:#2c3e50;--crystal-text-dark:hsla(0,0%,100%,.95);--crystal-shadow:rgba(0,0,0,.08);--crystal-shadow-dark:rgba(0,0,0,.25)}@keyframes crystalIn{0%{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translateY(0)}}@keyframes crystalPulse{0%{box-shadow:0 2px 8px var(--crystal-shadow)}50%{box-shadow:0 4px 12px var(--crystal-shadow)}to{box-shadow:0 2px 8px var(--crystal-shadow)}}.fl-crystal{animation:crystalIn .3s ease-out;background:var(--crystal-bg-light,var(--fl-bg-light));border-radius:var(--fl-border-radius,4px);box-shadow:0 2px 8px var(--crystal-shadow);font-family:var(--fl-font),serif;margin:0 0 1rem;max-width:380px;position:relative;transition:box-shadow .3s ease;will-change:transform,opacity}.fl-crystal:hover{animation:crystalPulse 2s ease-in-out infinite}.fl-crystal:last-child{margin-bottom:0}.fl-crystal .fl-content{align-items:center;display:flex;gap:.75rem;padding:1rem 2.5rem 1rem 1rem}.fl-crystal .fl-text{flex:1}.fl-crystal .fl-message{color:var(--crystal-text-light,var(--fl-text-light));font-size:.9375rem;line-height:1.4;margin:0}.fl-crystal .fl-close{background:none;border:none;color:currentColor;cursor:pointer;font-size:1.25rem;line-height:1;opacity:.5;padding:.25rem;position:absolute;right:.75rem;top:50%;touch-action:manipulation;transform:translateY(-50%);transition:all .2s ease}.fl-crystal .fl-close:focus,.fl-crystal .fl-close:hover{opacity:1;transform:translateY(-50%) scale(1.1)}.fl-crystal .fl-progress-bar{bottom:0;height:3px;left:0;overflow:hidden;position:absolute;right:0}.fl-crystal .fl-progress-bar .fl-progress{height:100%;transform-origin:left center;width:100%}.fl-crystal.fl-success,.fl-crystal.fl-success .fl-message{color:var(--success-color,var(--fl-success))}.fl-crystal.fl-error,.fl-crystal.fl-error .fl-message{color:var(--error-color,var(--fl-error))}.fl-crystal.fl-warning,.fl-crystal.fl-warning .fl-message{color:var(--warning-color,var(--fl-warning))}.fl-crystal.fl-info,.fl-crystal.fl-info .fl-message{color:var(--info-color,var(--fl-info))}.fl-crystal.fl-rtl{direction:rtl}.fl-crystal.fl-rtl .fl-content{padding:1rem 1rem 1rem 2.5rem}.fl-crystal.fl-rtl .fl-close{left:.75rem;right:auto}.fl-crystal.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-crystal{animation:none}.fl-crystal:hover{animation:none;box-shadow:0 2px 8px var(--crystal-shadow)}}.fl-crystal.fl-auto-dark,body.fl-dark .fl-crystal,html.fl-dark .fl-crystal{background-color:var(--crystal-bg-dark,var(--fl-bg-dark));box-shadow:0 2px 8px var(--crystal-shadow-dark)}.fl-crystal.fl-auto-dark .fl-message,body.fl-dark .fl-crystal .fl-message,html.fl-dark .fl-crystal .fl-message{color:var(--crystal-text-dark,var(--fl-text-dark))}.fl-crystal.fl-auto-dark:hover,body.fl-dark .fl-crystal:hover,html.fl-dark .fl-crystal:hover{animation:none;box-shadow:0 4px 16px var(--crystal-shadow-dark)}
.fl-crystal{--crystal-bg-light:#fff;--crystal-bg-dark:rgba(30,30,30,.95);--crystal-text-light:#2c3e50;--crystal-text-dark:hsla(0,0%,100%,.95);--crystal-shadow:rgba(0,0,0,.08);--crystal-shadow-dark:rgba(0,0,0,.25)}@keyframes crystalIn{0%{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translateY(0)}}@keyframes crystalPulse{0%{box-shadow:0 2px 8px var(--crystal-shadow)}50%{box-shadow:0 4px 12px var(--crystal-shadow)}to{box-shadow:0 2px 8px var(--crystal-shadow)}}.fl-crystal{animation:crystalIn .3s ease-out;background:var(--crystal-bg-light,var(--fl-bg-light));border-radius:var(--fl-border-radius,4px) var(--fl-border-radius,4px) 0 0;box-shadow:0 2px 8px var(--crystal-shadow);font-family:var(--fl-font),serif;margin:0 0 1rem;position:relative;transition:box-shadow .3s ease;will-change:transform,opacity}.fl-crystal:hover{animation:crystalPulse 2s ease-in-out infinite}.fl-crystal .fl-content{align-items:center;display:flex;gap:.75rem;padding:1rem 2.5rem 1rem 1rem}.fl-crystal .fl-text{flex:1}.fl-crystal .fl-message{color:var(--crystal-text-light,var(--fl-text-light));font-size:.9375rem;line-height:1.4;margin:0}.fl-crystal .fl-close{background:none;border:none;color:currentColor;cursor:pointer;font-size:1.25rem;line-height:1;opacity:.5;padding:.25rem;position:absolute;right:.75rem;top:50%;touch-action:manipulation;transform:translateY(-50%);transition:all .2s ease}.fl-crystal .fl-close:focus,.fl-crystal .fl-close:hover{opacity:1;transform:translateY(-50%) scale(1.1)}.fl-crystal .fl-progress-bar{bottom:0;height:3px;left:0;overflow:hidden;position:absolute;right:0}.fl-crystal .fl-progress-bar .fl-progress{height:100%;transform-origin:left center;width:100%}.fl-crystal.fl-success,.fl-crystal.fl-success .fl-message{color:var(--success-color,var(--fl-success))}.fl-crystal.fl-error,.fl-crystal.fl-error .fl-message{color:var(--error-color,var(--fl-error))}.fl-crystal.fl-warning,.fl-crystal.fl-warning .fl-message{color:var(--warning-color,var(--fl-warning))}.fl-crystal.fl-info,.fl-crystal.fl-info .fl-message{color:var(--info-color,var(--fl-info))}.fl-crystal.fl-rtl{direction:rtl}.fl-crystal.fl-rtl .fl-content{padding:1rem 1rem 1rem 2.5rem}.fl-crystal.fl-rtl .fl-close{left:.75rem;right:auto}.fl-crystal.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-crystal{animation:none}.fl-crystal:hover{animation:none;box-shadow:0 2px 8px var(--crystal-shadow)}}.fl-crystal.fl-auto-dark,body.fl-dark .fl-crystal,html.fl-dark .fl-crystal{background-color:var(--crystal-bg-dark,var(--fl-bg-dark));box-shadow:0 2px 8px var(--crystal-shadow-dark)}.fl-crystal.fl-auto-dark .fl-message,body.fl-dark .fl-crystal .fl-message,html.fl-dark .fl-crystal .fl-message{color:var(--crystal-text-dark,var(--fl-text-dark))}.fl-crystal.fl-auto-dark:hover,body.fl-dark .fl-crystal:hover,html.fl-dark .fl-crystal:hover{animation:none;box-shadow:0 4px 16px var(--crystal-shadow-dark)}
@@ -1 +1 @@
.fl-emerald{--emerald-bg-light:hsla(0,0%,100%,.9);--emerald-bg-dark:rgba(30,30,30,.9);--emerald-text-light:#333;--emerald-text-dark:hsla(0,0%,100%,.9);--emerald-shadow:rgba(0,0,0,.1);--emerald-blur:8px;--emerald-success:var(--success-color,#16a085);--emerald-error:var(--error-color,#e74c3c);--emerald-warning:var(--warning-color,#f39c12);--emerald-info:var(--info-color,#3498db)}@keyframes emeraldIn{0%{opacity:0;transform:scale(.5) translateY(20px)}60%{opacity:1;transform:scale(1.1) translateY(-5px)}to{opacity:1;transform:scale(1) translateY(0)}}.fl-emerald{animation:emeraldIn .5s cubic-bezier(.23,1,.32,1);backdrop-filter:blur(var(--emerald-blur));-webkit-backdrop-filter:blur(var(--emerald-blur));background:var(--emerald-bg-light);border-radius:10px;box-shadow:0 10px 20px var(--emerald-shadow);color:var(--emerald-text-light);font-family:"Inter",var(--fl-font),serif;margin:0 0 .5rem;overflow:hidden;padding:1rem 1.5rem 1rem 1rem;position:relative}.fl-emerald .fl-content{align-items:center;display:flex}.fl-emerald .fl-message{flex:1;font-size:.9rem;font-weight:500;line-height:1.4}.fl-emerald .fl-close{background:transparent;border:none;color:currentColor;cursor:pointer;font-size:1.3rem;margin-left:auto;opacity:.7;transition:opacity .2s ease}.fl-emerald .fl-close:focus,.fl-emerald .fl-close:hover{opacity:1}.fl-emerald.fl-success{color:var(--emerald-success)}.fl-emerald.fl-error{color:var(--emerald-error)}.fl-emerald.fl-warning{color:var(--emerald-warning)}.fl-emerald.fl-info{color:var(--emerald-info)}.fl-emerald.fl-rtl{direction:rtl;padding:1rem 1rem 1rem 1.5rem}.fl-emerald.fl-rtl .fl-content{flex-direction:row-reverse}.fl-emerald.fl-rtl .fl-close{margin-left:0;margin-right:auto}@media (prefers-reduced-motion:reduce){.fl-emerald{animation:none}}.fl-emerald.fl-auto-dark,body.fl-dark .fl-emerald,html.fl-dark .fl-emerald{background:var(--emerald-bg-dark)}.fl-emerald.fl-auto-dark .fl-message,body.fl-dark .fl-emerald .fl-message,html.fl-dark .fl-emerald .fl-message{color:var(--emerald-text-dark)}
.fl-emerald{--emerald-bg-light:#fff;--emerald-bg-dark:#1e1e1e;--emerald-text-light:#333;--emerald-text-dark:hsla(0,0%,100%,.9);--emerald-shadow:rgba(0,0,0,.1);--emerald-blur:8px;--emerald-success:var(--success-color,#16a085);--emerald-error:var(--error-color,#e74c3c);--emerald-warning:var(--warning-color,#f39c12);--emerald-info:var(--info-color,#3498db)}@keyframes emeraldIn{0%{opacity:0;transform:scale(.5) translateY(20px)}60%{opacity:1;transform:scale(1.1) translateY(-5px)}to{opacity:1;transform:scale(1) translateY(0)}}.fl-emerald{animation:emeraldIn .5s cubic-bezier(.23,1,.32,1);backdrop-filter:blur(var(--emerald-blur));-webkit-backdrop-filter:blur(var(--emerald-blur));background:var(--emerald-bg-light);border-radius:4px 4px 0 0;box-shadow:0 10px 20px var(--emerald-shadow);color:var(--emerald-text-light);font-family:"Inter",var(--fl-font),serif;margin:0 0 .5rem;overflow:hidden;padding:1rem 1.5rem 1rem 1rem;position:relative}.fl-emerald .fl-content{align-items:center;display:flex}.fl-emerald .fl-message{flex:1;font-size:.9rem;font-weight:500;line-height:1.4}.fl-emerald .fl-close{background:transparent;border:none;color:currentColor;cursor:pointer;font-size:1.3rem;margin-left:auto;opacity:.7;transition:opacity .2s ease}.fl-emerald .fl-close:focus,.fl-emerald .fl-close:hover{opacity:1}.fl-emerald.fl-success{color:var(--emerald-success)}.fl-emerald.fl-error{color:var(--emerald-error)}.fl-emerald.fl-warning{color:var(--emerald-warning)}.fl-emerald.fl-info{color:var(--emerald-info)}.fl-emerald.fl-rtl{direction:rtl;padding:1rem 1rem 1rem 1.5rem}.fl-emerald.fl-rtl .fl-content{flex-direction:row-reverse}.fl-emerald.fl-rtl .fl-close{margin-left:0;margin-right:auto}@media (prefers-reduced-motion:reduce){.fl-emerald{animation:none}}.fl-emerald.fl-auto-dark,body.fl-dark .fl-emerald,html.fl-dark .fl-emerald{background:var(--emerald-bg-dark)}.fl-emerald.fl-auto-dark .fl-message,body.fl-dark .fl-emerald .fl-message,html.fl-dark .fl-emerald .fl-message{color:var(--emerald-text-dark)}
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
!function(n,i){"object"==typeof exports&&"undefined"!=typeof module?i(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],i):i((n="undefined"!=typeof globalThis?globalThis:n||self).flasher)}(this,(function(n){"use strict";const i={render:n=>{var i;const{type:s,message:e}=n,l="error"===s||"warning"===s,t=l?"alert":"status",o=l?"assertive":"polite",r=String((null===(i=n.options)||void 0===i?void 0:i.timestamp)||"2025-03-02 06:49:21").split(" ")[1].substring(0,5);return`\n <div class="fl-facebook fl-${s}" role="${t}" aria-live="${o}" aria-atomic="true">\n <div class="fl-fb-notification">\n <div class="fl-icon-container">\n ${(()=>{switch(s){case"success":return'<div class="fl-fb-icon fl-fb-icon-success">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z"/>\n </svg>\n </div>';case"error":return'<div class="fl-fb-icon fl-fb-icon-error">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-11.5c-.69 0-1.25.56-1.25 1.25S11.31 13 12 13s1.25-.56 1.25-1.25S12.69 10.5 12 10.5zM12 9c.552 0 1-.448 1-1V7c0-.552-.448-1-1-1s-1 .448-1 1v1c0 .552.448 1 1 1z"/>\n </svg>\n </div>';case"warning":return'<div class="fl-fb-icon fl-fb-icon-warning">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12.865 3.00017L22.3912 19.5002C22.6674 19.9785 22.5035 20.5901 22.0252 20.8662C21.8732 20.954 21.7008 21.0002 21.5252 21.0002H2.47266C1.92037 21.0002 1.47266 20.5525 1.47266 20.0002C1.47266 19.8246 1.51886 19.6522 1.60663 19.5002L11.1329 3.00017C11.409 2.52187 12.0206 2.358 12.4989 2.63414C12.651 2.72192 12.7772 2.84815 12.865 3.00017ZM11 16.0002V18.0002H13V16.0002H11ZM11 8.00017V14.0002H13V8.00017H11Z"/>\n </svg>\n </div>';case"info":return'<div class="fl-fb-icon fl-fb-icon-info">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 100-16 8 8 0 000 16zm-1-5h2v-2h-2v2zm0-4h2V7h-2v4z"/>\n </svg>\n </div>'}return""})()}\n </div>\n <div class="fl-content">\n <div class="fl-message">\n ${e}\n </div>\n <div class="fl-meta">\n <span class="fl-time">${r}</span>\n </div>\n </div>\n <div class="fl-actions">\n <button class="fl-button fl-close" aria-label="Close ${s} message">\n <div class="fl-button-icon">\n <svg viewBox="0 0 24 24" width="20" height="20">\n <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>\n </svg>\n </div>\n </button>\n </div>\n </div>\n </div>`}};n.addTheme("facebook",i)}));
!function(n,i){"object"==typeof exports&&"undefined"!=typeof module?i(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],i):i((n="undefined"!=typeof globalThis?globalThis:n||self).flasher)}(this,(function(n){"use strict";const i={render:n=>{const{type:i,message:e}=n,s="error"===i||"warning"===i,t=s?"alert":"status",l=s?"assertive":"polite",o=(new Date).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"});return`\n <div class="fl-facebook fl-${i}" role="${t}" aria-live="${l}" aria-atomic="true">\n <div class="fl-fb-notification">\n <div class="fl-icon-container">\n ${(()=>{switch(i){case"success":return'<div class="fl-fb-icon fl-fb-icon-success">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z"/>\n </svg>\n </div>';case"error":return'<div class="fl-fb-icon fl-fb-icon-error">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-11.5c-.69 0-1.25.56-1.25 1.25S11.31 13 12 13s1.25-.56 1.25-1.25S12.69 10.5 12 10.5zM12 9c.552 0 1-.448 1-1V7c0-.552-.448-1-1-1s-1 .448-1 1v1c0 .552.448 1 1 1z"/>\n </svg>\n </div>';case"warning":return'<div class="fl-fb-icon fl-fb-icon-warning">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12.865 3.00017L22.3912 19.5002C22.6674 19.9785 22.5035 20.5901 22.0252 20.8662C21.8732 20.954 21.7008 21.0002 21.5252 21.0002H2.47266C1.92037 21.0002 1.47266 20.5525 1.47266 20.0002C1.47266 19.8246 1.51886 19.6522 1.60663 19.5002L11.1329 3.00017C11.409 2.52187 12.0206 2.358 12.4989 2.63414C12.651 2.72192 12.7772 2.84815 12.865 3.00017ZM11 16.0002V18.0002H13V16.0002H11ZM11 8.00017V14.0002H13V8.00017H11Z"/>\n </svg>\n </div>';case"info":return'<div class="fl-fb-icon fl-fb-icon-info">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 100-16 8 8 0 000 16zm-1-5h2v-2h-2v2zm0-4h2V7h-2v4z"/>\n </svg>\n </div>'}return""})()}\n </div>\n <div class="fl-content">\n <div class="fl-message">\n ${e}\n </div>\n <div class="fl-meta">\n <span class="fl-time">${o}</span>\n </div>\n </div>\n <div class="fl-actions">\n <button class="fl-button fl-close" aria-label="Close ${i} message">\n <div class="fl-button-icon">\n <svg viewBox="0 0 24 24" width="20" height="20">\n <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>\n </svg>\n </div>\n </button>\n </div>\n </div>\n </div>`}};n.addTheme("facebook",i)}));
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
.fl-google{--md-bg-light:#fff;--md-bg-dark:#2d2d2d;--md-text-light:rgba(0,0,0,.87);--md-text-secondary-light:rgba(0,0,0,.6);--md-text-dark:hsla(0,0%,100%,.87);--md-text-secondary-dark:hsla(0,0%,100%,.6);--md-elevation:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12);--md-elevation-dark:0 3px 5px -1px rgba(0,0,0,.4),0 6px 10px 0 rgba(0,0,0,.28),0 1px 18px 0 rgba(0,0,0,.24);--md-border-radius:4px;--md-success:#43a047;--md-info:#1e88e5;--md-warning:#fb8c00;--md-error:#e53935;--md-animation-duration:0.3s;--md-ripple-duration:0.6s}@keyframes mdSlideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes mdRipple{to{opacity:0;transform:scale(4)}}.fl-google{animation:mdSlideUp var(--md-animation-duration) cubic-bezier(.4,0,.2,1);font-family:Roboto,Segoe UI,Helvetica,Arial,sans-serif;margin:8px 0;max-width:400px;position:relative;width:100%}.fl-google .fl-md-card{background-color:var(--md-bg-light);border-radius:var(--md-border-radius);box-shadow:var(--md-elevation);color:var(--md-text-light);overflow:hidden}.fl-google .fl-content{align-items:flex-start;display:flex;padding:16px}.fl-google .fl-icon-wrapper{color:var(--md-text-secondary-light);flex-shrink:0;margin-right:16px}.fl-google .fl-text-content{flex:1}.fl-google .fl-title{font-size:1rem;font-weight:500;margin-bottom:4px}.fl-google .fl-message{color:var(--md-text-secondary-light);font-size:.875rem;line-height:1.43}.fl-google .fl-actions{display:flex;justify-content:flex-end;padding:8px}.fl-google .fl-action-button{background:transparent;border:none;border-radius:4px;color:currentColor;cursor:pointer;font-family:inherit;font-size:.8125rem;font-weight:500;letter-spacing:.0892857143em;overflow:hidden;padding:8px 12px;position:relative;text-transform:uppercase;transition:background-color .2s}.fl-google .fl-action-button:focus,.fl-google .fl-action-button:hover{background-color:rgba(0,0,0,.04)}.fl-google .fl-action-button:after{background:currentColor;border-radius:50%;content:"";height:5px;opacity:0;pointer-events:none;position:absolute;transform:scale(1);width:5px}.fl-google .fl-action-button:active:after{animation:mdRipple var(--md-ripple-duration) linear;opacity:.3}.fl-google.fl-success .fl-action-button,.fl-google.fl-success .fl-icon-wrapper{color:var(--md-success)}.fl-google.fl-info .fl-action-button,.fl-google.fl-info .fl-icon-wrapper{color:var(--md-info)}.fl-google.fl-warning .fl-action-button,.fl-google.fl-warning .fl-icon-wrapper{color:var(--md-warning)}.fl-google.fl-error .fl-action-button,.fl-google.fl-error .fl-icon-wrapper{color:var(--md-error)}.fl-google .fl-progress-bar{bottom:0;height:4px;left:0;overflow:hidden;position:absolute;right:0}.fl-google .fl-progress-bar .fl-progress{height:100%;transform-origin:left center;width:100%}.fl-google.fl-success .fl-progress{background-color:var(--md-success)}.fl-google.fl-info .fl-progress{background-color:var(--md-info)}.fl-google.fl-warning .fl-progress{background-color:var(--md-warning)}.fl-google.fl-error .fl-progress{background-color:var(--md-error)}.fl-google.fl-rtl{direction:rtl}.fl-google.fl-rtl .fl-content{flex-direction:row-reverse}.fl-google.fl-rtl .fl-icon-wrapper{margin-left:16px;margin-right:0}.fl-google.fl-rtl .fl-actions{justify-content:flex-start}.fl-google.fl-rtl .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-google,.fl-google .fl-action-button:active:after{animation:none}}.fl-google.fl-auto-dark .fl-md-card,body.fl-dark .fl-google .fl-md-card,html.fl-dark .fl-google .fl-md-card{background-color:var(--md-bg-dark);box-shadow:var(--md-elevation-dark);color:var(--md-text-dark)}.fl-google.fl-auto-dark .fl-message,body.fl-dark .fl-google .fl-message,html.fl-dark .fl-google .fl-message{color:var(--md-text-secondary-dark)}.fl-google.fl-auto-dark .fl-action-button:focus,.fl-google.fl-auto-dark .fl-action-button:hover,body.fl-dark .fl-google .fl-action-button:focus,body.fl-dark .fl-google .fl-action-button:hover,html.fl-dark .fl-google .fl-action-button:focus,html.fl-dark .fl-google .fl-action-button:hover{background-color:hsla(0,0%,100%,.08)}
.fl-google{--md-bg-light:#fff;--md-bg-dark:#2d2d2d;--md-text-light:rgba(0,0,0,.87);--md-text-secondary-light:rgba(0,0,0,.6);--md-text-dark:hsla(0,0%,100%,.87);--md-text-secondary-dark:hsla(0,0%,100%,.6);--md-elevation:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12);--md-elevation-dark:0 3px 5px -1px rgba(0,0,0,.4),0 6px 10px 0 rgba(0,0,0,.28),0 1px 18px 0 rgba(0,0,0,.24);--md-border-radius:4px;--md-success:#43a047;--md-info:#1e88e5;--md-warning:#fb8c00;--md-error:#e53935;--md-animation-duration:0.3s;--md-ripple-duration:0.6s}@keyframes mdSlideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes mdRipple{to{opacity:0;transform:scale(4)}}.fl-google{animation:mdSlideUp var(--md-animation-duration) cubic-bezier(.4,0,.2,1);font-family:Roboto,Segoe UI,Helvetica,Arial,sans-serif;margin:8px 0;position:relative}.fl-google .fl-md-card{background-color:var(--md-bg-light);border-radius:var(--md-border-radius);box-shadow:var(--md-elevation);color:var(--md-text-light);overflow:hidden}.fl-google .fl-content{align-items:flex-start;display:flex;padding:16px}.fl-google .fl-icon-wrapper{color:var(--md-text-secondary-light);flex-shrink:0;margin-right:16px}.fl-google .fl-text-content{flex:1}.fl-google .fl-title{font-size:1rem;font-weight:500;margin-bottom:4px}.fl-google .fl-message{color:var(--md-text-secondary-light);font-size:.875rem;line-height:1.43}.fl-google .fl-actions{display:flex;justify-content:flex-end;padding:8px}.fl-google .fl-action-button{background:transparent;border:none;border-radius:4px;color:currentColor;cursor:pointer;font-family:inherit;font-size:.8125rem;font-weight:500;letter-spacing:.0892857143em;overflow:hidden;padding:8px 12px;position:relative;text-transform:uppercase;transition:background-color .2s}.fl-google .fl-action-button:focus,.fl-google .fl-action-button:hover{background-color:rgba(0,0,0,.04)}.fl-google .fl-action-button:after{background:currentColor;border-radius:50%;content:"";height:5px;opacity:0;pointer-events:none;position:absolute;transform:scale(1);width:5px}.fl-google .fl-action-button:active:after{animation:mdRipple var(--md-ripple-duration) linear;opacity:.3}.fl-google.fl-success .fl-action-button,.fl-google.fl-success .fl-icon-wrapper{color:var(--md-success)}.fl-google.fl-info .fl-action-button,.fl-google.fl-info .fl-icon-wrapper{color:var(--md-info)}.fl-google.fl-warning .fl-action-button,.fl-google.fl-warning .fl-icon-wrapper{color:var(--md-warning)}.fl-google.fl-error .fl-action-button,.fl-google.fl-error .fl-icon-wrapper{color:var(--md-error)}.fl-google .fl-progress-bar{bottom:0;height:4px;left:0;overflow:hidden;position:absolute;right:0}.fl-google .fl-progress-bar .fl-progress{height:100%;transform-origin:left center;width:100%}.fl-google.fl-success .fl-progress{background-color:var(--md-success)}.fl-google.fl-info .fl-progress{background-color:var(--md-info)}.fl-google.fl-warning .fl-progress{background-color:var(--md-warning)}.fl-google.fl-error .fl-progress{background-color:var(--md-error)}.fl-google.fl-rtl{direction:rtl}.fl-google.fl-rtl .fl-content{flex-direction:row-reverse}.fl-google.fl-rtl .fl-icon-wrapper{margin-left:16px;margin-right:0}.fl-google.fl-rtl .fl-actions{justify-content:flex-start}.fl-google.fl-rtl .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-google,.fl-google .fl-action-button:active:after{animation:none}}.fl-google.fl-auto-dark .fl-md-card,body.fl-dark .fl-google .fl-md-card,html.fl-dark .fl-google .fl-md-card{background-color:var(--md-bg-dark);box-shadow:var(--md-elevation-dark);color:var(--md-text-dark)}.fl-google.fl-auto-dark .fl-message,body.fl-dark .fl-google .fl-message,html.fl-dark .fl-google .fl-message{color:var(--md-text-secondary-dark)}.fl-google.fl-auto-dark .fl-action-button:focus,.fl-google.fl-auto-dark .fl-action-button:hover,body.fl-dark .fl-google .fl-action-button:focus,body.fl-dark .fl-google .fl-action-button:hover,html.fl-dark .fl-google .fl-action-button:focus,html.fl-dark .fl-google .fl-action-button:hover{background-color:hsla(0,0%,100%,.08)}
+1 -1
View File
@@ -1 +1 @@
.fl-ios{--ios-bg-light:hsla(0,0%,100%,.85);--ios-bg-dark:rgba(30,30,30,.85);--ios-text-light:#000;--ios-text-secondary-light:#6e6e6e;--ios-text-dark:#fff;--ios-text-secondary-dark:#a8a8a8;--ios-border-radius:13px;--ios-shadow:0 2px 12px rgba(0,0,0,.15);--ios-shadow-dark:0 2px 12px rgba(0,0,0,.35);--ios-icon-size:18px;--ios-blur:30px;--ios-success:#34c759;--ios-info:#007aff;--ios-warning:#ff9500;--ios-error:#ff3b30;--ios-animation-duration:0.4s}@keyframes iosSlideIn{0%{opacity:0;transform:translateY(-15px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes iosExpand{0%{max-height:0;opacity:0}to{max-height:100px;opacity:1}}.fl-ios{animation:iosSlideIn var(--ios-animation-duration) cubic-bezier(.23,1,.32,1);font-family:-apple-system,BlinkMacSystemFont,San Francisco,Helvetica Neue,Helvetica,Arial,sans-serif;margin:10px 0;max-width:400px;position:relative;width:100%;will-change:transform,opacity}.fl-ios .fl-ios-notification{backdrop-filter:blur(var(--ios-blur));-webkit-backdrop-filter:blur(var(--ios-blur));background-color:var(--ios-bg-light);border-radius:var(--ios-border-radius);box-shadow:var(--ios-shadow);color:var(--ios-text-light);padding:12px 15px;position:relative}.fl-ios .fl-header{align-items:center;display:flex;margin-bottom:8px;padding-right:20px}.fl-ios .fl-app-icon{align-items:center;background-color:currentColor;border-radius:5px;display:flex;flex-shrink:0;height:22px;justify-content:center;margin-right:8px;width:22px}.fl-ios .fl-icon-svg{color:#fff;height:14px;width:14px}.fl-ios .fl-app-info{align-items:baseline;display:flex;flex:1;justify-content:space-between}.fl-ios .fl-app-name{font-size:.85rem;font-weight:600}.fl-ios .fl-time{color:var(--ios-text-secondary-light);flex-shrink:0;font-size:.75rem;margin-left:5px}.fl-ios .fl-content{animation:iosExpand .3s forwards;animation-delay:.1s;overflow:hidden}.fl-ios .fl-message{font-size:.95rem;line-height:1.3;margin:0;padding-right:15px}.fl-ios .fl-close{align-items:center;background-color:rgba(0,0,0,.1);border:none;border-radius:50%;color:var(--ios-text-light);cursor:pointer;display:flex;font-size:14px;height:18px;justify-content:center;line-height:1;opacity:.7;padding:0;position:absolute;right:12px;top:10px;transition:opacity .2s;width:18px}.fl-ios .fl-close:focus,.fl-ios .fl-close:hover{opacity:1}.fl-ios.fl-success .fl-app-icon{color:var(--ios-success)}.fl-ios.fl-info .fl-app-icon{color:var(--ios-info)}.fl-ios.fl-warning .fl-app-icon{color:var(--ios-warning)}.fl-ios.fl-error .fl-app-icon{color:var(--ios-error)}.fl-ios.fl-rtl{direction:rtl}.fl-ios.fl-rtl .fl-header{padding-left:20px;padding-right:0}.fl-ios.fl-rtl .fl-app-icon{margin-left:8px;margin-right:0}.fl-ios.fl-rtl .fl-time{margin-left:0;margin-right:5px}.fl-ios.fl-rtl .fl-message{padding-left:15px;padding-right:0}.fl-ios.fl-rtl .fl-close{left:12px;right:auto}@media (prefers-reduced-motion:reduce){.fl-ios{animation:none}.fl-ios .fl-content{animation:none;max-height:none;opacity:1}}@media screen and (max-width:480px){.fl-ios{width:100%}}.fl-ios.fl-auto-dark .fl-ios-notification,body.fl-dark .fl-ios .fl-ios-notification,html.fl-dark .fl-ios .fl-ios-notification{background-color:var(--ios-bg-dark);box-shadow:var(--ios-shadow-dark);color:var(--ios-text-dark)}.fl-ios.fl-auto-dark .fl-time,body.fl-dark .fl-ios .fl-time,html.fl-dark .fl-ios .fl-time{color:var(--ios-text-secondary-dark)}.fl-ios.fl-auto-dark .fl-close,body.fl-dark .fl-ios .fl-close,html.fl-dark .fl-ios .fl-close{background-color:hsla(0,0%,100%,.2);color:var(--ios-text-dark)}
.fl-ios{--ios-bg-light:#fff;--ios-bg-dark:#1e1e1e;--ios-text-light:#000;--ios-text-secondary-light:#6e6e6e;--ios-text-dark:#fff;--ios-text-secondary-dark:#a8a8a8;--ios-border-radius:13px;--ios-shadow:0 2px 12px rgba(0,0,0,.15);--ios-shadow-dark:0 2px 12px rgba(0,0,0,.35);--ios-icon-size:18px;--ios-blur:30px;--ios-success:#34c759;--ios-info:#007aff;--ios-warning:#ff9500;--ios-error:#ff3b30;--ios-animation-duration:0.4s}@keyframes iosSlideIn{0%{opacity:0;transform:translateY(-15px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes iosExpand{0%{max-height:0;opacity:0}to{max-height:100px;opacity:1}}.fl-ios{animation:iosSlideIn var(--ios-animation-duration) cubic-bezier(.23,1,.32,1);font-family:-apple-system,BlinkMacSystemFont,San Francisco,Helvetica Neue,Helvetica,Arial,sans-serif;margin:10px 0;position:relative;will-change:transform,opacity}.fl-ios .fl-ios-notification{backdrop-filter:blur(var(--ios-blur));-webkit-backdrop-filter:blur(var(--ios-blur));background-color:var(--ios-bg-light);border-radius:var(--ios-border-radius);box-shadow:var(--ios-shadow);color:var(--ios-text-light);padding:12px 15px;position:relative}.fl-ios .fl-header{align-items:center;display:flex;margin-bottom:8px;padding-right:20px}.fl-ios .fl-app-icon{align-items:center;background-color:currentColor;border-radius:5px;display:flex;flex-shrink:0;height:22px;justify-content:center;margin-right:8px;width:22px}.fl-ios .fl-icon-svg{color:#fff;height:14px;width:14px}.fl-ios .fl-app-info{align-items:baseline;display:flex;flex:1;justify-content:space-between}.fl-ios .fl-app-name{font-size:.85rem;font-weight:600}.fl-ios .fl-time{color:var(--ios-text-secondary-light);flex-shrink:0;font-size:.75rem;margin-left:5px}.fl-ios .fl-content{animation:iosExpand .3s forwards;animation-delay:.1s;overflow:hidden}.fl-ios .fl-message{font-size:.95rem;line-height:1.3;margin:0;padding-right:15px}.fl-ios .fl-close{align-items:center;background-color:rgba(0,0,0,.1);border:none;border-radius:50%;color:var(--ios-text-light);cursor:pointer;display:flex;font-size:14px;height:18px;justify-content:center;line-height:1;opacity:.7;padding:0;position:absolute;right:12px;top:10px;transition:opacity .2s;width:18px}.fl-ios .fl-close:focus,.fl-ios .fl-close:hover{opacity:1}.fl-ios.fl-success .fl-app-icon{color:var(--ios-success)}.fl-ios.fl-info .fl-app-icon{color:var(--ios-info)}.fl-ios.fl-warning .fl-app-icon{color:var(--ios-warning)}.fl-ios.fl-error .fl-app-icon{color:var(--ios-error)}.fl-ios.fl-rtl{direction:rtl}.fl-ios.fl-rtl .fl-header{padding-left:20px;padding-right:0}.fl-ios.fl-rtl .fl-app-icon{margin-left:8px;margin-right:0}.fl-ios.fl-rtl .fl-time{margin-left:0;margin-right:5px}.fl-ios.fl-rtl .fl-message{padding-left:15px;padding-right:0}.fl-ios.fl-rtl .fl-close{left:12px;right:auto}@media (prefers-reduced-motion:reduce){.fl-ios{animation:none}.fl-ios .fl-content{animation:none;max-height:none;opacity:1}}@media screen and (max-width:480px){.fl-ios{width:100%}}.fl-ios.fl-auto-dark .fl-ios-notification,body.fl-dark .fl-ios .fl-ios-notification,html.fl-dark .fl-ios .fl-ios-notification{background-color:var(--ios-bg-dark);box-shadow:var(--ios-shadow-dark);color:var(--ios-text-dark)}.fl-ios.fl-auto-dark .fl-time,body.fl-dark .fl-ios .fl-time,html.fl-dark .fl-ios .fl-time{color:var(--ios-text-secondary-dark)}.fl-ios.fl-auto-dark .fl-close,body.fl-dark .fl-ios .fl-close,html.fl-dark .fl-ios .fl-close{background-color:hsla(0,0%,100%,.2);color:var(--ios-text-dark)}
@@ -1 +1 @@
.fl-jade{--jade-text-light:#5f6c7b;--jade-text-dark:#e2e8f0;--jade-shadow:0 8px 24px hsla(210,8%,62%,.1);--jade-shadow-dark:0 8px 24px rgba(0,0,0,.2);--jade-border-radius:1rem;--jade-transition:0.3s cubic-bezier(0.4,0,0.2,1);--jade-success-bg:#f0fdf4;--jade-success-color:#16a34a;--jade-info-bg:#eff6ff;--jade-info-color:#3b82f6;--jade-warning-bg:#fffbeb;--jade-warning-color:#f59e0b;--jade-error-bg:#fef2f2;--jade-error-color:#dc2626;--jade-success-bg-dark:rgba(22,163,74,.15);--jade-info-bg-dark:rgba(59,130,246,.15);--jade-warning-bg-dark:rgba(245,158,11,.15);--jade-error-bg-dark:rgba(220,38,38,.15)}@keyframes jadeIn{0%{opacity:0;transform:translateY(-10px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}.fl-jade{animation:jadeIn .4s var(--jade-transition);border:1px solid transparent;border-radius:var(--jade-border-radius);box-shadow:var(--jade-shadow);font-family:var(--fl-font),serif;margin:.5rem 0;overflow:hidden;padding:1rem 1.25rem;position:relative;will-change:transform,opacity}.fl-jade:last-child{margin-bottom:0}.fl-jade .fl-content{align-items:center;display:flex}.fl-jade .fl-message{flex:1;font-size:.875rem;font-weight:500;line-height:1.5;padding-right:.75rem}.fl-jade .fl-close{align-items:center;background:none;border:none;border-radius:50%;cursor:pointer;display:flex;flex-shrink:0;font-size:1.125rem;height:1.875rem;justify-content:center;opacity:.6;padding:.375rem;transition:all var(--jade-transition);width:1.875rem}.fl-jade .fl-close:focus,.fl-jade .fl-close:hover{background-color:rgba(0,0,0,.05);opacity:1}.fl-jade .fl-progress-bar{border-radius:0 0 var(--jade-border-radius) var(--jade-border-radius);bottom:0;height:3px;left:0;opacity:.7;overflow:hidden;position:absolute;right:0}.fl-jade .fl-progress-bar .fl-progress{height:100%;width:100%}.fl-jade.fl-success{background-color:var(--jade-success-bg);border-color:rgba(22,163,74,.1)}.fl-jade.fl-success,.fl-jade.fl-success .fl-close{color:var(--jade-success-color)}.fl-jade.fl-success .fl-close:focus,.fl-jade.fl-success .fl-close:hover{background-color:rgba(22,163,74,.1)}.fl-jade.fl-success .fl-progress-bar .fl-progress{background-color:var(--jade-success-color)}.fl-jade.fl-info{background-color:var(--jade-info-bg);border-color:rgba(59,130,246,.1)}.fl-jade.fl-info,.fl-jade.fl-info .fl-close{color:var(--jade-info-color)}.fl-jade.fl-info .fl-close:focus,.fl-jade.fl-info .fl-close:hover{background-color:rgba(59,130,246,.1)}.fl-jade.fl-info .fl-progress-bar .fl-progress{background-color:var(--jade-info-color)}.fl-jade.fl-warning{background-color:var(--jade-warning-bg);border-color:rgba(245,158,11,.1)}.fl-jade.fl-warning,.fl-jade.fl-warning .fl-close{color:var(--jade-warning-color)}.fl-jade.fl-warning .fl-close:focus,.fl-jade.fl-warning .fl-close:hover{background-color:rgba(245,158,11,.1)}.fl-jade.fl-warning .fl-progress-bar .fl-progress{background-color:var(--jade-warning-color)}.fl-jade.fl-error{background-color:var(--jade-error-bg);border-color:rgba(220,38,38,.1)}.fl-jade.fl-error,.fl-jade.fl-error .fl-close{color:var(--jade-error-color)}.fl-jade.fl-error .fl-close:focus,.fl-jade.fl-error .fl-close:hover{background-color:rgba(220,38,38,.1)}.fl-jade.fl-error .fl-progress-bar .fl-progress{background-color:var(--jade-error-color)}.fl-jade.fl-rtl{direction:rtl}.fl-jade.fl-rtl .fl-message{padding-left:.75rem;padding-right:0}.fl-jade.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-jade{animation:none}}.fl-jade.fl-auto-dark,body.fl-dark .fl-jade,html.fl-dark .fl-jade{box-shadow:var(--jade-shadow-dark);color:var(--jade-text-dark)}.fl-jade.fl-auto-dark.fl-success,body.fl-dark .fl-jade.fl-success,html.fl-dark .fl-jade.fl-success{background-color:var(--jade-success-bg-dark);border-color:rgba(22,163,74,.2)}.fl-jade.fl-auto-dark.fl-info,body.fl-dark .fl-jade.fl-info,html.fl-dark .fl-jade.fl-info{background-color:var(--jade-info-bg-dark);border-color:rgba(59,130,246,.2)}.fl-jade.fl-auto-dark.fl-warning,body.fl-dark .fl-jade.fl-warning,html.fl-dark .fl-jade.fl-warning{background-color:var(--jade-warning-bg-dark);border-color:rgba(245,158,11,.2)}.fl-jade.fl-auto-dark.fl-error,body.fl-dark .fl-jade.fl-error,html.fl-dark .fl-jade.fl-error{background-color:var(--jade-error-bg-dark);border-color:rgba(220,38,38,.2)}.fl-jade.fl-auto-dark .fl-close:focus,.fl-jade.fl-auto-dark .fl-close:hover,body.fl-dark .fl-jade .fl-close:focus,body.fl-dark .fl-jade .fl-close:hover,html.fl-dark .fl-jade .fl-close:focus,html.fl-dark .fl-jade .fl-close:hover{background-color:hsla(0,0%,100%,.1)}
.fl-jade{--jade-text-light:#5f6c7b;--jade-text-dark:#e2e8f0;--jade-shadow:0 8px 24px hsla(210,8%,62%,.1);--jade-shadow-dark:0 8px 24px rgba(0,0,0,.2);--jade-border-radius:4px;--jade-transition:0.3s cubic-bezier(0.4,0,0.2,1);--jade-success-bg:#f0fdf4;--jade-success-color:#16a34a;--jade-info-bg:#eff6ff;--jade-info-color:#3b82f6;--jade-warning-bg:#fffbeb;--jade-warning-color:#f59e0b;--jade-error-bg:#fef2f2;--jade-error-color:#dc2626;--jade-success-bg-dark:rgba(22,163,74,.15);--jade-info-bg-dark:rgba(59,130,246,.15);--jade-warning-bg-dark:rgba(245,158,11,.15);--jade-error-bg-dark:rgba(220,38,38,.15)}@keyframes jadeIn{0%{opacity:0;transform:translateY(-10px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}.fl-jade{animation:jadeIn .4s var(--jade-transition);border:1px solid transparent;border-radius:var(--jade-border-radius) var(--jade-border-radius) 0 0;box-shadow:var(--jade-shadow);font-family:var(--fl-font),serif;margin:.5rem 0;overflow:hidden;padding:1rem 1.25rem;position:relative;will-change:transform,opacity}.fl-jade .fl-content{align-items:center;display:flex}.fl-jade .fl-message{flex:1;font-size:.875rem;font-weight:500;line-height:1.5;padding-right:.75rem}.fl-jade .fl-close{align-items:center;background:none;border:none;border-radius:50%;cursor:pointer;display:flex;flex-shrink:0;font-size:1.125rem;height:1.875rem;justify-content:center;opacity:.6;padding:.375rem;transition:all var(--jade-transition);width:1.875rem}.fl-jade .fl-close:focus,.fl-jade .fl-close:hover{background-color:rgba(0,0,0,.05);opacity:1}.fl-jade .fl-progress-bar{border-radius:0 0 var(--jade-border-radius) var(--jade-border-radius);bottom:0;height:3px;left:0;opacity:.7;overflow:hidden;position:absolute;right:0}.fl-jade .fl-progress-bar .fl-progress{height:100%;width:100%}.fl-jade.fl-success{background-color:var(--jade-success-bg);border-color:rgba(22,163,74,.1)}.fl-jade.fl-success,.fl-jade.fl-success .fl-close{color:var(--jade-success-color)}.fl-jade.fl-success .fl-close:focus,.fl-jade.fl-success .fl-close:hover{background-color:rgba(22,163,74,.1)}.fl-jade.fl-success .fl-progress-bar .fl-progress{background-color:var(--jade-success-color)}.fl-jade.fl-info{background-color:var(--jade-info-bg);border-color:rgba(59,130,246,.1)}.fl-jade.fl-info,.fl-jade.fl-info .fl-close{color:var(--jade-info-color)}.fl-jade.fl-info .fl-close:focus,.fl-jade.fl-info .fl-close:hover{background-color:rgba(59,130,246,.1)}.fl-jade.fl-info .fl-progress-bar .fl-progress{background-color:var(--jade-info-color)}.fl-jade.fl-warning{background-color:var(--jade-warning-bg);border-color:rgba(245,158,11,.1)}.fl-jade.fl-warning,.fl-jade.fl-warning .fl-close{color:var(--jade-warning-color)}.fl-jade.fl-warning .fl-close:focus,.fl-jade.fl-warning .fl-close:hover{background-color:rgba(245,158,11,.1)}.fl-jade.fl-warning .fl-progress-bar .fl-progress{background-color:var(--jade-warning-color)}.fl-jade.fl-error{background-color:var(--jade-error-bg);border-color:rgba(220,38,38,.1)}.fl-jade.fl-error,.fl-jade.fl-error .fl-close{color:var(--jade-error-color)}.fl-jade.fl-error .fl-close:focus,.fl-jade.fl-error .fl-close:hover{background-color:rgba(220,38,38,.1)}.fl-jade.fl-error .fl-progress-bar .fl-progress{background-color:var(--jade-error-color)}.fl-jade.fl-rtl{direction:rtl}.fl-jade.fl-rtl .fl-message{padding-left:.75rem;padding-right:0}.fl-jade.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-jade{animation:none}}.fl-jade.fl-auto-dark,body.fl-dark .fl-jade,html.fl-dark .fl-jade{box-shadow:var(--jade-shadow-dark);color:var(--jade-text-dark)}.fl-jade.fl-auto-dark.fl-success,body.fl-dark .fl-jade.fl-success,html.fl-dark .fl-jade.fl-success{background-color:var(--jade-success-bg-dark);border-color:rgba(22,163,74,.2)}.fl-jade.fl-auto-dark.fl-info,body.fl-dark .fl-jade.fl-info,html.fl-dark .fl-jade.fl-info{background-color:var(--jade-info-bg-dark);border-color:rgba(59,130,246,.2)}.fl-jade.fl-auto-dark.fl-warning,body.fl-dark .fl-jade.fl-warning,html.fl-dark .fl-jade.fl-warning{background-color:var(--jade-warning-bg-dark);border-color:rgba(245,158,11,.2)}.fl-jade.fl-auto-dark.fl-error,body.fl-dark .fl-jade.fl-error,html.fl-dark .fl-jade.fl-error{background-color:var(--jade-error-bg-dark);border-color:rgba(220,38,38,.2)}.fl-jade.fl-auto-dark .fl-close:focus,.fl-jade.fl-auto-dark .fl-close:hover,body.fl-dark .fl-jade .fl-close:focus,body.fl-dark .fl-jade .fl-close:hover,html.fl-dark .fl-jade .fl-close:focus,html.fl-dark .fl-jade .fl-close:hover{background-color:hsla(0,0%,100%,.1)}
@@ -1 +1 @@
.fl-material{--md-bg-light:#fff;--md-bg-dark:#2d2d2d;--md-text-light:rgba(0,0,0,.87);--md-text-secondary-light:rgba(0,0,0,.6);--md-text-dark:hsla(0,0%,100%,.87);--md-text-secondary-dark:hsla(0,0%,100%,.6);--md-elevation:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12);--md-elevation-dark:0 3px 5px -1px rgba(0,0,0,.4),0 6px 10px 0 rgba(0,0,0,.28),0 1px 18px 0 rgba(0,0,0,.24);--md-border-radius:4px;--md-success:#43a047;--md-info:#1e88e5;--md-warning:#fb8c00;--md-error:#e53935;--md-animation-duration:0.3s;--md-ripple-duration:0.6s}@keyframes mdSlideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes mdRipple{to{opacity:0;transform:scale(4)}}.fl-material{animation:mdSlideUp var(--md-animation-duration) cubic-bezier(.4,0,.2,1);font-family:Roboto,Segoe UI,Helvetica,Arial,sans-serif;margin:8px 0;max-width:400px;position:relative;width:100%}.fl-material .fl-md-card{background-color:var(--md-bg-light);border-radius:var(--md-border-radius);box-shadow:var(--md-elevation);color:var(--md-text-light);overflow:hidden}.fl-material .fl-content{align-items:flex-start;display:flex;padding:16px}.fl-material .fl-text-content{flex:1}.fl-material .fl-message{color:var(--md-text-secondary-light);font-size:.875rem;line-height:1.43}.fl-material .fl-actions{display:flex;justify-content:flex-end;padding:8px}.fl-material .fl-action-button{background:transparent;border:none;border-radius:4px;color:currentColor;cursor:pointer;font-family:inherit;font-size:.8125rem;font-weight:500;letter-spacing:.0892857143em;overflow:hidden;padding:8px 12px;position:relative;text-transform:uppercase;transition:background-color .2s}.fl-material .fl-action-button:focus,.fl-material .fl-action-button:hover{background-color:rgba(0,0,0,.04)}.fl-material .fl-action-button:after{background:currentColor;border-radius:50%;content:"";height:5px;opacity:0;pointer-events:none;position:absolute;transform:scale(1);width:5px}.fl-material .fl-action-button:active:after{animation:mdRipple var(--md-ripple-duration) linear;opacity:.3}.fl-material.fl-success .fl-action-button,.fl-material.fl-success .fl-icon-wrapper{color:var(--md-success)}.fl-material.fl-info .fl-action-button,.fl-material.fl-info .fl-icon-wrapper{color:var(--md-info)}.fl-material.fl-warning .fl-action-button,.fl-material.fl-warning .fl-icon-wrapper{color:var(--md-warning)}.fl-material.fl-error .fl-action-button,.fl-material.fl-error .fl-icon-wrapper{color:var(--md-error)}.fl-material .fl-progress-bar{bottom:0;height:4px;left:0;overflow:hidden;position:absolute;right:0}.fl-material .fl-progress-bar .fl-progress{height:100%;transform-origin:left center;width:100%}.fl-material.fl-success .fl-progress{background-color:var(--md-success)}.fl-material.fl-info .fl-progress{background-color:var(--md-info)}.fl-material.fl-warning .fl-progress{background-color:var(--md-warning)}.fl-material.fl-error .fl-progress{background-color:var(--md-error)}.fl-material.fl-rtl{direction:rtl}.fl-material.fl-rtl .fl-content{flex-direction:row-reverse}.fl-material.fl-rtl .fl-icon-wrapper{margin-left:16px;margin-right:0}.fl-material.fl-rtl .fl-actions{justify-content:flex-start}.fl-material.fl-rtl .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-material,.fl-material .fl-action-button:active:after{animation:none}}.fl-material.fl-auto-dark .fl-md-card,body.fl-dark .fl-material .fl-md-card,html.fl-dark .fl-material .fl-md-card{background-color:var(--md-bg-dark);box-shadow:var(--md-elevation-dark);color:var(--md-text-dark)}.fl-material.fl-auto-dark .fl-message,body.fl-dark .fl-material .fl-message,html.fl-dark .fl-material .fl-message{color:var(--md-text-secondary-dark)}.fl-material.fl-auto-dark .fl-action-button:focus,.fl-material.fl-auto-dark .fl-action-button:hover,body.fl-dark .fl-material .fl-action-button:focus,body.fl-dark .fl-material .fl-action-button:hover,html.fl-dark .fl-material .fl-action-button:focus,html.fl-dark .fl-material .fl-action-button:hover{background-color:hsla(0,0%,100%,.08)}
.fl-material{--md-bg-light:#fff;--md-bg-dark:#2d2d2d;--md-text-light:rgba(0,0,0,.87);--md-text-secondary-light:rgba(0,0,0,.6);--md-text-dark:hsla(0,0%,100%,.87);--md-text-secondary-dark:hsla(0,0%,100%,.6);--md-elevation:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12);--md-elevation-dark:0 3px 5px -1px rgba(0,0,0,.4),0 6px 10px 0 rgba(0,0,0,.28),0 1px 18px 0 rgba(0,0,0,.24);--md-border-radius:4px;--md-success:#43a047;--md-info:#1e88e5;--md-warning:#fb8c00;--md-error:#e53935;--md-animation-duration:0.3s;--md-ripple-duration:0.6s}@keyframes mdSlideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes mdRipple{to{opacity:0;transform:scale(4)}}.fl-material{animation:mdSlideUp var(--md-animation-duration) cubic-bezier(.4,0,.2,1);font-family:Roboto,Segoe UI,Helvetica,Arial,sans-serif;margin:8px 0;position:relative}.fl-material .fl-md-card{background-color:var(--md-bg-light);border-radius:var(--md-border-radius);box-shadow:var(--md-elevation);color:var(--md-text-light);overflow:hidden}.fl-material .fl-content{align-items:flex-start;display:flex;padding:16px}.fl-material .fl-text-content{flex:1}.fl-material .fl-message{color:var(--md-text-secondary-light);font-size:.875rem;line-height:1.43}.fl-material .fl-actions{display:flex;justify-content:flex-end;padding:8px}.fl-material .fl-action-button{background:transparent;border:none;border-radius:4px;color:currentColor;cursor:pointer;font-family:inherit;font-size:.8125rem;font-weight:500;letter-spacing:.0892857143em;overflow:hidden;padding:8px 12px;position:relative;text-transform:uppercase;transition:background-color .2s}.fl-material .fl-action-button:focus,.fl-material .fl-action-button:hover{background-color:rgba(0,0,0,.04)}.fl-material .fl-action-button:after{background:currentColor;border-radius:50%;content:"";height:5px;opacity:0;pointer-events:none;position:absolute;transform:scale(1);width:5px}.fl-material .fl-action-button:active:after{animation:mdRipple var(--md-ripple-duration) linear;opacity:.3}.fl-material.fl-success .fl-action-button,.fl-material.fl-success .fl-icon-wrapper{color:var(--md-success)}.fl-material.fl-info .fl-action-button,.fl-material.fl-info .fl-icon-wrapper{color:var(--md-info)}.fl-material.fl-warning .fl-action-button,.fl-material.fl-warning .fl-icon-wrapper{color:var(--md-warning)}.fl-material.fl-error .fl-action-button,.fl-material.fl-error .fl-icon-wrapper{color:var(--md-error)}.fl-material .fl-progress-bar{bottom:0;height:4px;left:0;overflow:hidden;position:absolute;right:0}.fl-material .fl-progress-bar .fl-progress{height:100%;transform-origin:left center;width:100%}.fl-material.fl-success .fl-progress{background-color:var(--md-success)}.fl-material.fl-info .fl-progress{background-color:var(--md-info)}.fl-material.fl-warning .fl-progress{background-color:var(--md-warning)}.fl-material.fl-error .fl-progress{background-color:var(--md-error)}.fl-material.fl-rtl{direction:rtl}.fl-material.fl-rtl .fl-content{flex-direction:row-reverse}.fl-material.fl-rtl .fl-icon-wrapper{margin-left:16px;margin-right:0}.fl-material.fl-rtl .fl-actions{justify-content:flex-start}.fl-material.fl-rtl .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-material,.fl-material .fl-action-button:active:after{animation:none}}.fl-material.fl-auto-dark .fl-md-card,body.fl-dark .fl-material .fl-md-card,html.fl-dark .fl-material .fl-md-card{background-color:var(--md-bg-dark);box-shadow:var(--md-elevation-dark);color:var(--md-text-dark)}.fl-material.fl-auto-dark .fl-message,body.fl-dark .fl-material .fl-message,html.fl-dark .fl-material .fl-message{color:var(--md-text-secondary-dark)}.fl-material.fl-auto-dark .fl-action-button:focus,.fl-material.fl-auto-dark .fl-action-button:hover,body.fl-dark .fl-material .fl-action-button:focus,body.fl-dark .fl-material .fl-action-button:hover,html.fl-dark .fl-material .fl-action-button:focus,html.fl-dark .fl-material .fl-action-button:hover{background-color:hsla(0,0%,100%,.08)}
@@ -1 +1 @@
.fl-minimal{--minimal-bg-light:hsla(0,0%,100%,.8);--minimal-bg-dark:rgba(25,25,25,.8);--minimal-text-light:#333;--minimal-text-dark:#f5f5f5;--minimal-shadow:0 2px 8px rgba(0,0,0,.08);--minimal-shadow-dark:0 2px 8px rgba(0,0,0,.15);--minimal-border-radius:6px;--minimal-border-color:rgba(0,0,0,.05);--minimal-border-color-dark:hsla(0,0%,100%,.1);--minimal-success:rgba(34,197,94,.9);--minimal-info:rgba(14,165,233,.9);--minimal-warning:rgba(245,158,11,.9);--minimal-error:rgba(239,68,68,.9)}@keyframes minimalIn{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.fl-minimal{animation:minimalIn .2s ease-out;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);background-color:var(--minimal-bg-light);border:1px solid var(--minimal-border-color);border-radius:var(--minimal-border-radius);box-shadow:var(--minimal-shadow);color:var(--minimal-text-light);font-family:-apple-system,BlinkMacSystemFont,var(--fl-font),sans-serif;margin:.5rem 0;max-width:320px;padding:.75rem 1rem;position:relative;will-change:transform,opacity}.fl-minimal:last-child{margin-bottom:0}.fl-minimal .fl-content{align-items:center;display:flex;gap:.75rem}.fl-minimal .fl-dot{border-radius:50%;flex-shrink:0;height:8px;width:8px}.fl-minimal .fl-message{flex:1;font-size:.875rem;font-weight:450;line-height:1.4;margin:0}.fl-minimal .fl-close{align-items:center;background:none;border:none;color:currentColor;cursor:pointer;display:flex;flex-shrink:0;font-size:1rem;height:1.5rem;justify-content:center;opacity:.5;padding:.25rem;transition:opacity .15s;width:1.5rem}.fl-minimal .fl-close:focus,.fl-minimal .fl-close:hover{opacity:.8}.fl-minimal .fl-progress-bar{border-radius:0 0 var(--minimal-border-radius) var(--minimal-border-radius);bottom:0;height:2px;left:0;opacity:.7;overflow:hidden;position:absolute;right:0}.fl-minimal .fl-progress-bar .fl-progress{height:100%;width:100%}.fl-minimal.fl-success .fl-dot,.fl-minimal.fl-success .fl-progress-bar .fl-progress{background-color:var(--minimal-success)}.fl-minimal.fl-info .fl-dot,.fl-minimal.fl-info .fl-progress-bar .fl-progress{background-color:var(--minimal-info)}.fl-minimal.fl-warning .fl-dot,.fl-minimal.fl-warning .fl-progress-bar .fl-progress{background-color:var(--minimal-warning)}.fl-minimal.fl-error .fl-dot,.fl-minimal.fl-error .fl-progress-bar .fl-progress{background-color:var(--minimal-error)}.fl-minimal.fl-rtl{direction:rtl}.fl-minimal.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-minimal{animation:none}}.fl-minimal.fl-auto-dark,body.fl-dark .fl-minimal,html.fl-dark .fl-minimal{background-color:var(--minimal-bg-dark);border-color:var(--minimal-border-color-dark);box-shadow:var(--minimal-shadow-dark);color:var(--minimal-text-dark)}
.fl-minimal{--minimal-bg-light:#fff;--minimal-bg-dark:#191919;--minimal-text-light:#333;--minimal-text-dark:#f5f5f5;--minimal-shadow:0 2px 8px rgba(0,0,0,.08);--minimal-shadow-dark:0 2px 8px rgba(0,0,0,.15);--minimal-border-radius:4px;--minimal-border-color:rgba(0,0,0,.05);--minimal-border-color-dark:hsla(0,0%,100%,.1);--minimal-success:rgba(34,197,94,.9);--minimal-info:rgba(14,165,233,.9);--minimal-warning:rgba(245,158,11,.9);--minimal-error:rgba(239,68,68,.9)}@keyframes minimalIn{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.fl-minimal{animation:minimalIn .2s ease-out;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);background-color:var(--minimal-bg-light);border:1px solid var(--minimal-border-color);border-radius:var(--minimal-border-radius);box-shadow:var(--minimal-shadow);color:var(--minimal-text-light);font-family:-apple-system,BlinkMacSystemFont,var(--fl-font),sans-serif;margin:.5rem 0;padding:.75rem 1rem;position:relative;will-change:transform,opacity}.fl-minimal .fl-content{align-items:center;display:flex;gap:.75rem}.fl-minimal .fl-dot{border-radius:50%;flex-shrink:0;height:8px;width:8px}.fl-minimal .fl-message{flex:1;font-size:.875rem;font-weight:450;line-height:1.4;margin:0}.fl-minimal .fl-close{align-items:center;background:none;border:none;color:currentColor;cursor:pointer;display:flex;flex-shrink:0;font-size:1rem;height:1.5rem;justify-content:center;opacity:.5;padding:.25rem;transition:opacity .15s;width:1.5rem}.fl-minimal .fl-close:focus,.fl-minimal .fl-close:hover{opacity:.8}.fl-minimal .fl-progress-bar{border-radius:0 0 var(--minimal-border-radius) var(--minimal-border-radius);bottom:0;height:2px;left:0;opacity:.7;overflow:hidden;position:absolute;right:0}.fl-minimal .fl-progress-bar .fl-progress{height:100%;width:100%}.fl-minimal.fl-success .fl-dot,.fl-minimal.fl-success .fl-progress-bar .fl-progress{background-color:var(--minimal-success)}.fl-minimal.fl-info .fl-dot,.fl-minimal.fl-info .fl-progress-bar .fl-progress{background-color:var(--minimal-info)}.fl-minimal.fl-warning .fl-dot,.fl-minimal.fl-warning .fl-progress-bar .fl-progress{background-color:var(--minimal-warning)}.fl-minimal.fl-error .fl-dot,.fl-minimal.fl-error .fl-progress-bar .fl-progress{background-color:var(--minimal-error)}.fl-minimal.fl-rtl{direction:rtl}.fl-minimal.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-minimal{animation:none}}.fl-minimal.fl-auto-dark,body.fl-dark .fl-minimal,html.fl-dark .fl-minimal{background-color:var(--minimal-bg-dark);border-color:var(--minimal-border-color-dark);box-shadow:var(--minimal-shadow-dark);color:var(--minimal-text-dark)}
@@ -1 +1 @@
.fl-neon{--neon-bg-light:hsla(0,0%,100%,.9);--neon-bg-dark:rgba(15,23,42,.9);--neon-text-light:#334155;--neon-text-dark:#f1f5f9;--neon-shadow:0 8px 30px rgba(0,0,0,.12);--neon-shadow-dark:0 8px 30px rgba(0,0,0,.25);--neon-border-radius:12px;--neon-success:#10b981;--neon-info:#3b82f6;--neon-warning:#f59e0b;--neon-error:#ef4444;--neon-glow-strength:10px;--neon-animation-duration:0.35s}@keyframes neonEntrance{0%{filter:blur(3px);opacity:0;transform:translateY(-15px)}to{filter:blur(0);opacity:1;transform:translateY(0)}}@keyframes neonGlow{0%,to{filter:drop-shadow(0 0 var(--neon-glow-strength) currentColor)}50%{filter:drop-shadow(0 0 calc(var(--neon-glow-strength)*.7) currentColor)}}.fl-neon{animation:neonEntrance var(--neon-animation-duration) ease-out;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);background-color:var(--neon-bg-light);border-radius:var(--neon-border-radius);box-shadow:var(--neon-shadow);color:var(--neon-text-light);font-family:"Inter",var(--fl-font),sans-serif;margin:12px 0;padding:14px 18px;position:relative;will-change:transform,opacity,filter}.fl-neon .fl-icon-box{align-items:center;animation:neonGlow 3s ease-in-out infinite;border-radius:50%;display:flex;height:24px;justify-content:center;left:16px;position:absolute;top:-12px;width:24px}.fl-neon .fl-icon-box:before{border-radius:50%;content:"";height:100%;opacity:.4;position:absolute;width:100%}.fl-neon .fl-icon-box:after{border-radius:50%;content:"";height:10px;position:relative;width:10px;z-index:1}.fl-neon .fl-content{align-items:center;display:flex}.fl-neon .fl-message{flex:1;font-size:.9375rem;font-weight:500;line-height:1.5}.fl-neon .fl-close{align-items:center;background:none;border:none;border-radius:50%;color:inherit;cursor:pointer;display:flex;flex-shrink:0;font-size:1.2rem;height:28px;justify-content:center;margin-left:16px;opacity:.6;padding:0;transition:all .2s ease;width:28px}.fl-neon .fl-close:focus,.fl-neon .fl-close:hover{background-color:rgba(0,0,0,.06);opacity:1}.fl-neon .fl-progress-bar{border-radius:0 0 var(--neon-border-radius) var(--neon-border-radius);bottom:0;height:3px;left:0;overflow:hidden;position:absolute;right:0}.fl-neon .fl-progress-bar .fl-progress{height:100%;width:100%}.fl-neon.fl-success .fl-icon-box{color:var(--neon-success)}.fl-neon.fl-success .fl-icon-box:after,.fl-neon.fl-success .fl-icon-box:before,.fl-neon.fl-success .fl-progress{background-color:var(--neon-success)}.fl-neon.fl-info .fl-icon-box{color:var(--neon-info)}.fl-neon.fl-info .fl-icon-box:after,.fl-neon.fl-info .fl-icon-box:before,.fl-neon.fl-info .fl-progress{background-color:var(--neon-info)}.fl-neon.fl-warning .fl-icon-box{color:var(--neon-warning)}.fl-neon.fl-warning .fl-icon-box:after,.fl-neon.fl-warning .fl-icon-box:before,.fl-neon.fl-warning .fl-progress{background-color:var(--neon-warning)}.fl-neon.fl-error .fl-icon-box{color:var(--neon-error)}.fl-neon.fl-error .fl-icon-box:after,.fl-neon.fl-error .fl-icon-box:before,.fl-neon.fl-error .fl-progress{background-color:var(--neon-error)}.fl-neon.fl-rtl{direction:rtl}.fl-neon.fl-rtl .fl-icon-box{left:auto;right:16px}.fl-neon.fl-rtl .fl-close{margin-left:0;margin-right:16px}.fl-neon.fl-rtl .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-neon,.fl-neon .fl-icon-box{animation:none}}.fl-neon.fl-auto-dark,body.fl-dark .fl-neon,html.fl-dark .fl-neon{background-color:var(--neon-bg-dark);box-shadow:var(--neon-shadow-dark);color:var(--neon-text-dark)}.fl-neon.fl-auto-dark .fl-close:focus,.fl-neon.fl-auto-dark .fl-close:hover,body.fl-dark .fl-neon .fl-close:focus,body.fl-dark .fl-neon .fl-close:hover,html.fl-dark .fl-neon .fl-close:focus,html.fl-dark .fl-neon .fl-close:hover{background-color:hsla(0,0%,100%,.1)}
.fl-neon{--neon-bg-light:#fff;--neon-bg-dark:#0f172a;--neon-text-light:#334155;--neon-text-dark:#f1f5f9;--neon-shadow:0 8px 30px rgba(0,0,0,.12);--neon-shadow-dark:0 8px 30px rgba(0,0,0,.25);--neon-border-radius:4px;--neon-success:#10b981;--neon-info:#3b82f6;--neon-warning:#f59e0b;--neon-error:#ef4444;--neon-glow-strength:10px;--neon-animation-duration:0.35s}@keyframes neonEntrance{0%{filter:blur(3px);opacity:0;transform:translateY(-15px)}to{filter:blur(0);opacity:1;transform:translateY(0)}}@keyframes neonGlow{0%,to{filter:drop-shadow(0 0 var(--neon-glow-strength) currentColor)}50%{filter:drop-shadow(0 0 calc(var(--neon-glow-strength)*.7) currentColor)}}.fl-neon{animation:neonEntrance var(--neon-animation-duration) ease-out;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);background-color:var(--neon-bg-light);border-radius:var(--neon-border-radius) var(--neon-border-radius) 0 0;box-shadow:var(--neon-shadow);color:var(--neon-text-light);font-family:"Inter",var(--fl-font),sans-serif;margin:12px 0;overflow:hidden;padding:14px 18px;position:relative;will-change:transform,opacity,filter}.fl-neon .fl-icon-box{align-items:center;animation:neonGlow 3s ease-in-out infinite;border-radius:50%;display:flex;height:24px;justify-content:center;left:16px;position:absolute;top:-12px;width:24px}.fl-neon .fl-icon-box:before{border-radius:50%;content:"";height:100%;opacity:.4;position:absolute;width:100%}.fl-neon .fl-icon-box:after{border-radius:50%;content:"";height:10px;position:relative;width:10px;z-index:1}.fl-neon .fl-content{align-items:center;display:flex}.fl-neon .fl-message{flex:1;font-size:.9375rem;font-weight:500;line-height:1.5}.fl-neon .fl-close{align-items:center;background:none;border:none;border-radius:50%;color:inherit;cursor:pointer;display:flex;flex-shrink:0;font-size:1.2rem;height:28px;justify-content:center;margin-left:16px;opacity:.6;padding:0;transition:all .2s ease;width:28px}.fl-neon .fl-close:focus,.fl-neon .fl-close:hover{background-color:rgba(0,0,0,.06);opacity:1}.fl-neon .fl-progress-bar{border-radius:0 0 var(--neon-border-radius) var(--neon-border-radius);bottom:0;height:3px;left:0;overflow:hidden;position:absolute;right:0}.fl-neon .fl-progress-bar .fl-progress{height:100%;width:100%}.fl-neon.fl-success .fl-icon-box{color:var(--neon-success)}.fl-neon.fl-success .fl-icon-box:after,.fl-neon.fl-success .fl-icon-box:before,.fl-neon.fl-success .fl-progress{background-color:var(--neon-success)}.fl-neon.fl-info .fl-icon-box{color:var(--neon-info)}.fl-neon.fl-info .fl-icon-box:after,.fl-neon.fl-info .fl-icon-box:before,.fl-neon.fl-info .fl-progress{background-color:var(--neon-info)}.fl-neon.fl-warning .fl-icon-box{color:var(--neon-warning)}.fl-neon.fl-warning .fl-icon-box:after,.fl-neon.fl-warning .fl-icon-box:before,.fl-neon.fl-warning .fl-progress{background-color:var(--neon-warning)}.fl-neon.fl-error .fl-icon-box{color:var(--neon-error)}.fl-neon.fl-error .fl-icon-box:after,.fl-neon.fl-error .fl-icon-box:before,.fl-neon.fl-error .fl-progress{background-color:var(--neon-error)}.fl-neon.fl-rtl{direction:rtl}.fl-neon.fl-rtl .fl-icon-box{left:auto;right:16px}.fl-neon.fl-rtl .fl-close{margin-left:0;margin-right:16px}.fl-neon.fl-rtl .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-neon,.fl-neon .fl-icon-box{animation:none}}.fl-neon.fl-auto-dark,body.fl-dark .fl-neon,html.fl-dark .fl-neon{background-color:var(--neon-bg-dark);box-shadow:var(--neon-shadow-dark);color:var(--neon-text-dark)}.fl-neon.fl-auto-dark .fl-close:focus,.fl-neon.fl-auto-dark .fl-close:hover,body.fl-dark .fl-neon .fl-close:focus,body.fl-dark .fl-neon .fl-close:hover,html.fl-dark .fl-neon .fl-close:focus,html.fl-dark .fl-neon .fl-close:hover{background-color:hsla(0,0%,100%,.1)}
@@ -1 +1 @@
.fl-onyx{--onyx-bg-light:#fff;--onyx-bg-dark:#1e1e1e;--onyx-text-light:#333;--onyx-text-dark:#f5f5f5;--onyx-shadow:0 8px 30px rgba(0,0,0,.12);--onyx-shadow-dark:0 8px 30px rgba(0,0,0,.25);--onyx-border-radius:1rem;--onyx-success:#10b981;--onyx-info:#3b82f6;--onyx-warning:#f59e0b;--onyx-error:#ef4444}@keyframes onyxIn{0%{filter:blur(3px);opacity:0;transform:translateY(15px)}to{filter:blur(0);opacity:1;transform:translateY(0)}}.fl-onyx{animation:onyxIn .4s cubic-bezier(.16,1,.3,1);background-color:var(--onyx-bg-light);border-radius:var(--onyx-border-radius);box-shadow:var(--onyx-shadow);color:var(--onyx-text-light);font-family:var(--fl-font),serif;margin:.75rem 0;overflow:hidden;padding:1rem 1.25rem;position:relative;will-change:transform,opacity,filter}.fl-onyx:after,.fl-onyx:before{border-radius:50%;content:"";height:6px;position:absolute;width:6px;z-index:1}.fl-onyx:before{left:10px;top:10px}.fl-onyx:after{bottom:10px;right:10px}.fl-onyx .fl-content{align-items:center;display:flex;padding-left:.4rem}.fl-onyx .fl-text{flex:1;position:relative}.fl-onyx .fl-message{font-size:.925rem;font-weight:400;letter-spacing:.01rem;line-height:1.5}.fl-onyx .fl-close{align-items:center;background:none;border:none;border-radius:50%;color:currentColor;cursor:pointer;display:flex;flex-shrink:0;font-size:1.25rem;height:1.75rem;justify-content:center;margin-left:1rem;opacity:.6;padding:.25rem;transition:all .2s ease;width:1.75rem}.fl-onyx .fl-close:focus,.fl-onyx .fl-close:hover{background-color:rgba(0,0,0,.05);opacity:1}.fl-onyx .fl-progress-bar{bottom:0;height:3px;left:0;overflow:hidden;position:absolute;right:0}.fl-onyx .fl-progress-bar .fl-progress{height:100%;transform-origin:left center;width:100%}.fl-onyx.fl-success .fl-progress-bar .fl-progress,.fl-onyx.fl-success:after,.fl-onyx.fl-success:before{background-color:var(--onyx-success)}.fl-onyx.fl-info .fl-progress-bar .fl-progress,.fl-onyx.fl-info:after,.fl-onyx.fl-info:before{background-color:var(--onyx-info)}.fl-onyx.fl-warning .fl-progress-bar .fl-progress,.fl-onyx.fl-warning:after,.fl-onyx.fl-warning:before{background-color:var(--onyx-warning)}.fl-onyx.fl-error .fl-progress-bar .fl-progress,.fl-onyx.fl-error:after,.fl-onyx.fl-error:before{background-color:var(--onyx-error)}.fl-onyx.fl-rtl{direction:rtl}.fl-onyx.fl-rtl .fl-content{padding-left:0;padding-right:.4rem}.fl-onyx.fl-rtl .fl-close{margin-left:0;margin-right:1rem}.fl-onyx.fl-rtl:before{left:auto;right:10px}.fl-onyx.fl-rtl:after{left:10px;right:auto}.fl-onyx.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-onyx{animation:none}}.fl-onyx.fl-auto-dark,body.fl-dark .fl-onyx,html.fl-dark .fl-onyx{background-color:var(--onyx-bg-dark);box-shadow:var(--onyx-shadow-dark);color:var(--onyx-text-dark)}.fl-onyx.fl-auto-dark .fl-close:focus,.fl-onyx.fl-auto-dark .fl-close:hover,body.fl-dark .fl-onyx .fl-close:focus,body.fl-dark .fl-onyx .fl-close:hover,html.fl-dark .fl-onyx .fl-close:focus,html.fl-dark .fl-onyx .fl-close:hover{background-color:hsla(0,0%,100%,.1)}
.fl-onyx{--onyx-bg-light:#fff;--onyx-bg-dark:#1e1e1e;--onyx-text-light:#333;--onyx-text-dark:#f5f5f5;--onyx-shadow:0 8px 30px rgba(0,0,0,.12);--onyx-shadow-dark:0 8px 30px rgba(0,0,0,.25);--onyx-border-radius:4px;--onyx-success:#10b981;--onyx-info:#3b82f6;--onyx-warning:#f59e0b;--onyx-error:#ef4444}@keyframes onyxIn{0%{filter:blur(3px);opacity:0;transform:translateY(15px)}to{filter:blur(0);opacity:1;transform:translateY(0)}}.fl-onyx{animation:onyxIn .4s cubic-bezier(.16,1,.3,1);background-color:var(--onyx-bg-light);border-radius:var(--onyx-border-radius) var(--onyx-border-radius) 0 0;box-shadow:var(--onyx-shadow);color:var(--onyx-text-light);font-family:var(--fl-font),serif;margin:.75rem 0;overflow:hidden;padding:1rem 1.25rem;position:relative;will-change:transform,opacity,filter}.fl-onyx:after,.fl-onyx:before{border-radius:50%;content:"";height:6px;position:absolute;width:6px;z-index:1}.fl-onyx:before{left:10px;top:10px}.fl-onyx:after{bottom:10px;right:10px}.fl-onyx .fl-content{align-items:center;display:flex;padding-left:.4rem}.fl-onyx .fl-text{flex:1;position:relative}.fl-onyx .fl-message{font-size:.925rem;font-weight:400;letter-spacing:.01rem;line-height:1.5}.fl-onyx .fl-close{align-items:center;background:none;border:none;border-radius:50%;color:currentColor;cursor:pointer;display:flex;flex-shrink:0;font-size:1.25rem;height:1.75rem;justify-content:center;margin-left:1rem;opacity:.6;padding:.25rem;transition:all .2s ease;width:1.75rem}.fl-onyx .fl-close:focus,.fl-onyx .fl-close:hover{background-color:rgba(0,0,0,.05);opacity:1}.fl-onyx .fl-progress-bar{bottom:0;height:3px;left:0;overflow:hidden;position:absolute;right:0}.fl-onyx .fl-progress-bar .fl-progress{height:100%;transform-origin:left center;width:100%}.fl-onyx.fl-success .fl-progress-bar .fl-progress,.fl-onyx.fl-success:after,.fl-onyx.fl-success:before{background-color:var(--onyx-success)}.fl-onyx.fl-info .fl-progress-bar .fl-progress,.fl-onyx.fl-info:after,.fl-onyx.fl-info:before{background-color:var(--onyx-info)}.fl-onyx.fl-warning .fl-progress-bar .fl-progress,.fl-onyx.fl-warning:after,.fl-onyx.fl-warning:before{background-color:var(--onyx-warning)}.fl-onyx.fl-error .fl-progress-bar .fl-progress,.fl-onyx.fl-error:after,.fl-onyx.fl-error:before{background-color:var(--onyx-error)}.fl-onyx.fl-rtl{direction:rtl}.fl-onyx.fl-rtl .fl-content{padding-left:0;padding-right:.4rem}.fl-onyx.fl-rtl .fl-close{margin-left:0;margin-right:1rem}.fl-onyx.fl-rtl:before{left:auto;right:10px}.fl-onyx.fl-rtl:after{left:10px;right:auto}.fl-onyx.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-onyx{animation:none}}.fl-onyx.fl-auto-dark,body.fl-dark .fl-onyx,html.fl-dark .fl-onyx{background-color:var(--onyx-bg-dark);box-shadow:var(--onyx-shadow-dark);color:var(--onyx-text-dark)}.fl-onyx.fl-auto-dark .fl-close:focus,.fl-onyx.fl-auto-dark .fl-close:hover,body.fl-dark .fl-onyx .fl-close:focus,body.fl-dark .fl-onyx .fl-close:hover,html.fl-dark .fl-onyx .fl-close:focus,html.fl-dark .fl-onyx .fl-close:hover{background-color:hsla(0,0%,100%,.1)}
@@ -1 +1 @@
.fl-ruby{--ruby-text:#fff;--ruby-text-dark:#f8fafc;--ruby-border-radius:1.125rem;--ruby-shadow:0 10px 25px -3px rgba(0,0,0,.2);--ruby-success-gradient:linear-gradient(135deg,#059669,#10b981);--ruby-info-gradient:linear-gradient(135deg,#2563eb,#3b82f6);--ruby-warning-gradient:linear-gradient(135deg,#d97706,#f59e0b);--ruby-error-gradient:linear-gradient(135deg,#b91c1c,#ef4444)}@keyframes rubyShine{0%{left:-100%;opacity:.6}60%{left:100%;opacity:.6}to{left:100%;opacity:0}}@keyframes rubyIn{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}.fl-ruby{animation:rubyIn .35s cubic-bezier(.21,1.02,.73,1);border-radius:var(--ruby-border-radius);box-shadow:var(--ruby-shadow);color:var(--ruby-text);font-family:var(--fl-font),serif;margin:.75rem 0;overflow:hidden;padding:0;position:relative;will-change:transform,opacity}.fl-ruby .fl-shine{animation:rubyShine 6s infinite;animation-delay:1s;background:linear-gradient(90deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.3) 50%,hsla(0,0%,100%,0));height:100%;left:-100%;position:absolute;top:0;transform:skewX(-20deg);width:50%;z-index:1}.fl-ruby .fl-content{align-items:center;display:flex;padding:.9rem 1.1rem;position:relative;z-index:2}.fl-ruby .fl-icon-circle{align-items:center;background-color:hsla(0,0%,100%,.25);border-radius:50%;display:flex;flex-shrink:0;height:2.25rem;justify-content:center;margin-right:1rem;width:2.25rem}.fl-ruby .fl-icon{background-color:transparent;color:var(--ruby-text);font-size:1rem;margin:0}.fl-ruby .fl-text{flex:1}.fl-ruby .fl-message{font-size:.925rem;font-weight:500;line-height:1.5}.fl-ruby .fl-close{align-items:center;background:hsla(0,0%,100%,.2);border:none;border-radius:50%;color:var(--ruby-text);cursor:pointer;display:flex;flex-shrink:0;font-size:1.1rem;height:1.6rem;justify-content:center;margin-left:.75rem;opacity:.8;padding:.1rem;transition:all .2s;width:1.6rem}.fl-ruby .fl-close:focus,.fl-ruby .fl-close:hover{background:hsla(0,0%,100%,.3);opacity:1;transform:scale(1.05)}.fl-ruby .fl-progress-bar{background-color:rgba(0,0,0,.1);bottom:0;height:5px;left:0;overflow:hidden;position:absolute;right:0;z-index:3}.fl-ruby .fl-progress-bar .fl-progress{background:hsla(0,0%,100%,.4);height:100%;width:100%}.fl-ruby.fl-success{background:var(--ruby-success-gradient)}.fl-ruby.fl-info{background:var(--ruby-info-gradient)}.fl-ruby.fl-warning{background:var(--ruby-warning-gradient)}.fl-ruby.fl-error{background:var(--ruby-error-gradient)}.fl-ruby.fl-rtl{direction:rtl}.fl-ruby.fl-rtl .fl-icon-circle{margin-left:1rem;margin-right:0}.fl-ruby.fl-rtl .fl-close{margin-left:0;margin-right:.75rem}.fl-ruby.fl-rtl .fl-shine{transform:skewX(20deg)}.fl-ruby.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-ruby{animation:none}.fl-ruby .fl-shine{display:none}}
.fl-ruby{--ruby-text:#fff;--ruby-text-dark:#f8fafc;--ruby-border-radius:4px;--ruby-shadow:0 10px 25px -3px rgba(0,0,0,.2);--ruby-success-gradient:linear-gradient(135deg,#059669,#10b981);--ruby-info-gradient:linear-gradient(135deg,#2563eb,#3b82f6);--ruby-warning-gradient:linear-gradient(135deg,#d97706,#f59e0b);--ruby-error-gradient:linear-gradient(135deg,#b91c1c,#ef4444)}@keyframes rubyShine{0%{left:-100%;opacity:.6}60%{left:100%;opacity:.6}to{left:100%;opacity:0}}@keyframes rubyIn{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}.fl-ruby{animation:rubyIn .35s cubic-bezier(.21,1.02,.73,1);border-radius:var(--ruby-border-radius) var(--ruby-border-radius) 0 0;box-shadow:var(--ruby-shadow);color:var(--ruby-text);font-family:var(--fl-font),serif;margin:.75rem 0;overflow:hidden;padding:0;position:relative;will-change:transform,opacity}.fl-ruby .fl-shine{animation:rubyShine 6s infinite;animation-delay:1s;background:linear-gradient(90deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.3) 50%,hsla(0,0%,100%,0));height:100%;left:-100%;position:absolute;top:0;transform:skewX(-20deg);width:50%;z-index:1}.fl-ruby .fl-content{align-items:center;display:flex;padding:.9rem 1.1rem;position:relative;z-index:2}.fl-ruby .fl-icon-circle{align-items:center;background-color:hsla(0,0%,100%,.25);border-radius:50%;display:flex;flex-shrink:0;height:2.25rem;justify-content:center;margin-right:1rem;width:2.25rem}.fl-ruby .fl-icon{background-color:transparent;color:var(--ruby-text);font-size:1rem;margin:0}.fl-ruby .fl-text{flex:1}.fl-ruby .fl-message{font-size:.925rem;font-weight:500;line-height:1.5}.fl-ruby .fl-close{align-items:center;background:hsla(0,0%,100%,.2);border:none;border-radius:50%;color:var(--ruby-text);cursor:pointer;display:flex;flex-shrink:0;font-size:1.1rem;height:1.6rem;justify-content:center;margin-left:.75rem;opacity:.8;padding:.1rem;transition:all .2s;width:1.6rem}.fl-ruby .fl-close:focus,.fl-ruby .fl-close:hover{background:hsla(0,0%,100%,.3);opacity:1;transform:scale(1.05)}.fl-ruby .fl-progress-bar{background-color:rgba(0,0,0,.1);bottom:0;height:5px;left:0;overflow:hidden;position:absolute;right:0;z-index:3}.fl-ruby .fl-progress-bar .fl-progress{background:hsla(0,0%,100%,.4);height:100%;width:100%}.fl-ruby.fl-success{background:var(--ruby-success-gradient)}.fl-ruby.fl-info{background:var(--ruby-info-gradient)}.fl-ruby.fl-warning{background:var(--ruby-warning-gradient)}.fl-ruby.fl-error{background:var(--ruby-error-gradient)}.fl-ruby.fl-rtl{direction:rtl}.fl-ruby.fl-rtl .fl-icon-circle{margin-left:1rem;margin-right:0}.fl-ruby.fl-rtl .fl-close{margin-left:0;margin-right:.75rem}.fl-ruby.fl-rtl .fl-shine{transform:skewX(20deg)}.fl-ruby.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-ruby{animation:none}.fl-ruby .fl-shine{display:none}}
@@ -1 +1 @@
.fl-sapphire{--sapphire-bg-base:rgba(30,30,30,.9);--sapphire-text:#f0f0f0;--sapphire-shadow:rgba(0,0,0,.15);--sapphire-progress-bg:hsla(0,0%,100%,.2);--sapphire-progress-fill:hsla(0,0%,100%,.9);--sapphire-success:rgba(16,185,129,.95);--sapphire-error:rgba(239,68,68,.95);--sapphire-warning:rgba(245,158,11,.95);--sapphire-info:rgba(59,130,246,.95);--sapphire-animation:0.4s cubic-bezier(0.25,0.46,0.45,0.94)}@keyframes sapphireIn{0%{opacity:0;transform:translateY(10px)}60%{transform:translateY(-3px)}to{opacity:1;transform:translateY(0)}}.fl-sapphire{animation:sapphireIn var(--sapphire-animation);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);background-color:var(--sapphire-bg-base);border-radius:.5em;box-shadow:0 6px 16px var(--sapphire-shadow);color:var(--sapphire-text);font-family:Roboto,var(--fl-font),serif;margin:0 0 .75em;min-width:200px;padding:1em 1.5em;position:relative;transition:all .3s ease;will-change:transform,opacity}.fl-sapphire:last-child{margin-bottom:0}.fl-sapphire .fl-message{color:var(--sapphire-text);font-size:.925em;line-height:1.4}.fl-sapphire .fl-progress-bar{background-color:var(--sapphire-progress-bg);border-radius:0 0 .375em .375em;bottom:0;height:4px;left:0;overflow:hidden;position:absolute;right:0}.fl-sapphire .fl-progress-bar .fl-progress{background-color:var(--sapphire-progress-fill);height:100%;transform-origin:left center;width:100%;will-change:transform}.fl-sapphire.fl-success{background-color:var(--sapphire-success)}.fl-sapphire.fl-error{background-color:var(--sapphire-error)}.fl-sapphire.fl-warning{background-color:var(--sapphire-warning)}.fl-sapphire.fl-info{background-color:var(--sapphire-info)}.fl-sapphire.fl-rtl{direction:rtl}.fl-sapphire.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-sapphire{animation:none}}
.fl-sapphire{--sapphire-bg-base:rgba(30,30,30,.9);--sapphire-text:#f0f0f0;--sapphire-shadow:rgba(0,0,0,.15);--sapphire-progress-bg:hsla(0,0%,100%,.2);--sapphire-progress-fill:hsla(0,0%,100%,.9);--sapphire-success:rgba(16,185,129,.95);--sapphire-error:rgba(239,68,68,.95);--sapphire-warning:rgba(245,158,11,.95);--sapphire-info:rgba(59,130,246,.95);--sapphire-animation:0.4s cubic-bezier(0.25,0.46,0.45,0.94)}@keyframes sapphireIn{0%{opacity:0;transform:translateY(10px)}60%{transform:translateY(-3px)}to{opacity:1;transform:translateY(0)}}.fl-sapphire{animation:sapphireIn var(--sapphire-animation);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);background-color:var(--sapphire-bg-base);border-radius:4px 4px 0 0;box-shadow:0 6px 16px var(--sapphire-shadow);color:var(--sapphire-text);font-family:Roboto,var(--fl-font),serif;margin:0 0 .75em;padding:1em 1.5em;position:relative;transition:all .3s ease;will-change:transform,opacity}.fl-sapphire .fl-message{color:var(--sapphire-text);font-size:.925em;line-height:1.4}.fl-sapphire .fl-progress-bar{background-color:var(--sapphire-progress-bg);border-radius:0 0 .375em .375em;bottom:0;height:4px;left:0;overflow:hidden;position:absolute;right:0}.fl-sapphire .fl-progress-bar .fl-progress{background-color:var(--sapphire-progress-fill);height:100%;transform-origin:left center;width:100%;will-change:transform}.fl-sapphire.fl-success{background-color:var(--sapphire-success)}.fl-sapphire.fl-error{background-color:var(--sapphire-error)}.fl-sapphire.fl-warning{background-color:var(--sapphire-warning)}.fl-sapphire.fl-info{background-color:var(--sapphire-info)}.fl-sapphire.fl-rtl{direction:rtl}.fl-sapphire.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-sapphire{animation:none}}
@@ -1 +1 @@
.fl-slack{--slack-bg-light:#fff;--slack-bg-dark:#1a1d21;--slack-hover-light:#f8f8f8;--slack-hover-dark:#222529;--slack-text-light:#1d1c1d;--slack-text-secondary-light:#616061;--slack-text-dark:#e0e0e0;--slack-text-secondary-dark:#ababad;--slack-border-light:#e0e0e0;--slack-border-dark:#393a3e;--slack-shadow:0 1px 0 rgba(0,0,0,.1);--slack-shadow-dark:0 1px 0 rgba(0,0,0,.2);--slack-avatar-size:36px;--slack-success:#2bac76;--slack-info:#1264a3;--slack-warning:#e8912d;--slack-error:#e01e5a;--slack-animation-duration:150ms}@keyframes slackFadeIn{0%{opacity:0}to{opacity:1}}.fl-slack{animation:slackFadeIn var(--slack-animation-duration) ease-out;font-family:Lato,Slack-Lato,Helvetica Neue,Helvetica,sans-serif;margin:4px 0;max-width:500px;position:relative;width:100%}.fl-slack .fl-slack-message{align-items:flex-start;background-color:var(--slack-bg-light);border:1px solid var(--slack-border-light);border-radius:4px;box-shadow:var(--slack-shadow);color:var(--slack-text-light);display:flex;padding:8px 20px 8px 8px;transition:background-color .1s ease}.fl-slack .fl-slack-message:hover{background-color:var(--slack-hover-light)}.fl-slack .fl-avatar{align-items:center;background-color:currentColor;border-radius:4px;display:flex;flex-shrink:0;height:var(--slack-avatar-size);justify-content:center;margin-right:8px;width:var(--slack-avatar-size)}.fl-slack .fl-type-icon{color:#fff;font-size:16px;font-weight:700}.fl-slack .fl-message-content{flex:1;min-width:0}.fl-slack .fl-message-text{font-size:15px;line-height:1.46668;word-break:break-word}.fl-slack .fl-actions{opacity:0;position:absolute;right:6px;top:8px;transition:opacity .1s ease;visibility:hidden}.fl-slack .fl-slack-message:hover .fl-actions{opacity:1;visibility:visible}.fl-slack .fl-close{align-items:center;background:none;border:none;border-radius:4px;color:var(--slack-text-secondary-light);cursor:pointer;display:flex;justify-content:center;padding:4px}.fl-slack .fl-close:hover{background-color:var(--slack-hover-light);color:var(--slack-text-light)}.fl-slack.fl-success .fl-avatar{color:var(--slack-success)}.fl-slack.fl-info .fl-avatar{color:var(--slack-info)}.fl-slack.fl-warning .fl-avatar{color:var(--slack-warning)}.fl-slack.fl-error .fl-avatar{color:var(--slack-error)}.fl-slack.fl-rtl{direction:rtl}.fl-slack.fl-rtl .fl-avatar{margin-left:8px;margin-right:0}.fl-slack.fl-rtl .fl-username{margin-left:4px;margin-right:0}.fl-slack.fl-rtl .fl-actions{left:6px;right:auto}.fl-slack.fl-rtl .fl-slack-message{padding:8px 8px 8px 20px}@media (prefers-reduced-motion:reduce){.fl-slack{animation:none}}.fl-slack.fl-auto-dark .fl-slack-message,body.fl-dark .fl-slack .fl-slack-message,html.fl-dark .fl-slack .fl-slack-message{background-color:var(--slack-bg-dark);border-color:var(--slack-border-dark);box-shadow:var(--slack-shadow-dark);color:var(--slack-text-dark)}.fl-slack.fl-auto-dark .fl-slack-message:hover,body.fl-dark .fl-slack .fl-slack-message:hover,html.fl-dark .fl-slack .fl-slack-message:hover{background-color:var(--slack-hover-dark)}.fl-slack.fl-auto-dark .fl-close,body.fl-dark .fl-slack .fl-close,html.fl-dark .fl-slack .fl-close{color:var(--slack-text-secondary-dark)}.fl-slack.fl-auto-dark .fl-close:hover,body.fl-dark .fl-slack .fl-close:hover,html.fl-dark .fl-slack .fl-close:hover{background-color:var(--slack-hover-dark);color:var(--slack-text-dark)}
.fl-slack{--slack-bg-light:#fff;--slack-bg-dark:#1a1d21;--slack-hover-light:#f8f8f8;--slack-hover-dark:#222529;--slack-text-light:#1d1c1d;--slack-text-secondary-light:#616061;--slack-text-dark:#e0e0e0;--slack-text-secondary-dark:#ababad;--slack-border-light:#e0e0e0;--slack-border-dark:#393a3e;--slack-shadow:0 1px 0 rgba(0,0,0,.1);--slack-shadow-dark:0 1px 0 rgba(0,0,0,.2);--slack-avatar-size:36px;--slack-success:#2bac76;--slack-info:#1264a3;--slack-warning:#e8912d;--slack-error:#e01e5a;--slack-animation-duration:150ms}@keyframes slackFadeIn{0%{opacity:0}to{opacity:1}}.fl-slack{animation:slackFadeIn var(--slack-animation-duration) ease-out;font-family:Lato,Slack-Lato,Helvetica Neue,Helvetica,sans-serif;margin:4px 0;position:relative}.fl-slack .fl-slack-message{align-items:flex-start;background-color:var(--slack-bg-light);border:1px solid var(--slack-border-light);border-radius:4px;box-shadow:var(--slack-shadow);color:var(--slack-text-light);display:flex;padding:8px 20px 8px 8px;transition:background-color .1s ease}.fl-slack .fl-slack-message:hover{background-color:var(--slack-hover-light)}.fl-slack .fl-avatar{align-items:center;background-color:currentColor;border-radius:4px;display:flex;flex-shrink:0;height:var(--slack-avatar-size);justify-content:center;margin-right:8px;width:var(--slack-avatar-size)}.fl-slack .fl-type-icon{color:#fff;font-size:16px;font-weight:700}.fl-slack .fl-message-content{flex:1;min-width:0}.fl-slack .fl-message-text{font-size:15px;line-height:1.46668;word-break:break-word}.fl-slack .fl-actions{opacity:0;position:absolute;right:6px;top:8px;transition:opacity .1s ease;visibility:hidden}.fl-slack .fl-slack-message:hover .fl-actions{opacity:1;visibility:visible}.fl-slack .fl-close{align-items:center;background:none;border:none;border-radius:4px;color:var(--slack-text-secondary-light);cursor:pointer;display:flex;justify-content:center;padding:4px}.fl-slack .fl-close:hover{background-color:var(--slack-hover-light);color:var(--slack-text-light)}.fl-slack.fl-success .fl-avatar{color:var(--slack-success)}.fl-slack.fl-info .fl-avatar{color:var(--slack-info)}.fl-slack.fl-warning .fl-avatar{color:var(--slack-warning)}.fl-slack.fl-error .fl-avatar{color:var(--slack-error)}.fl-slack.fl-rtl{direction:rtl}.fl-slack.fl-rtl .fl-avatar{margin-left:8px;margin-right:0}.fl-slack.fl-rtl .fl-username{margin-left:4px;margin-right:0}.fl-slack.fl-rtl .fl-actions{left:6px;right:auto}.fl-slack.fl-rtl .fl-slack-message{padding:8px 8px 8px 20px}@media (prefers-reduced-motion:reduce){.fl-slack{animation:none}}.fl-slack.fl-auto-dark .fl-slack-message,body.fl-dark .fl-slack .fl-slack-message,html.fl-dark .fl-slack .fl-slack-message{background-color:var(--slack-bg-dark);border-color:var(--slack-border-dark);box-shadow:var(--slack-shadow-dark);color:var(--slack-text-dark)}.fl-slack.fl-auto-dark .fl-slack-message:hover,body.fl-dark .fl-slack .fl-slack-message:hover,html.fl-dark .fl-slack .fl-slack-message:hover{background-color:var(--slack-hover-dark)}.fl-slack.fl-auto-dark .fl-close,body.fl-dark .fl-slack .fl-close,html.fl-dark .fl-slack .fl-close{color:var(--slack-text-secondary-dark)}.fl-slack.fl-auto-dark .fl-close:hover,body.fl-dark .fl-slack .fl-close:hover,html.fl-dark .fl-slack .fl-close:hover{background-color:var(--slack-hover-dark);color:var(--slack-text-dark)}
@@ -0,0 +1,147 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ExamplesController extends AbstractController
{
#[Route('/examples', name: 'app_examples')]
public function index(): Response
{
flash()->info('Browse through our examples to see PHPFlasher in action!');
return $this->render('examples/index.html.twig');
}
#[Route('/examples/form', name: 'app_form_example')]
public function formExample(Request $request): Response
{
if ($request->isMethod('POST')) {
$name = $request->request->get('name');
$email = $request->request->get('email');
// Simulate validation
$errors = [];
if (empty($name)) {
$errors['name'] = 'Name is required';
}
if (empty($email)) {
$errors['email'] = 'Email is required';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Invalid email format';
}
// Display appropriate notifications
if (empty($errors)) {
flash()->success('Form submitted successfully! Thank you, ' . $name);
} else {
foreach ($errors as $field => $message) {
flash()->error($message);
}
}
}
return $this->render('examples/form.html.twig');
}
#[Route('/examples/ajax', name: 'app_ajax_example')]
public function ajaxExample(): Response
{
return $this->render('examples/ajax.html.twig');
}
#[Route('/examples/ajax/process', name: 'app_ajax_process', methods: ['POST'])]
public function ajaxProcess(Request $request): JsonResponse
{
if (!$request->isXmlHttpRequest()) {
return new JsonResponse(['error' => 'AJAX requests only'], 400);
}
$action = $request->request->get('action');
$success = mt_rand(0, 10) > 2; // 80% success rate for demo
if ($success) {
switch ($action) {
case 'save':
flash()->success('Data saved successfully!');
break;
case 'update':
flash()->success('Record updated successfully!');
break;
case 'delete':
flash()->info('Item has been deleted.');
break;
default:
flash()->success('Operation completed successfully!');
}
return new JsonResponse(['success' => true]);
} else {
// Simulate errors
flash()->error('An error occurred while processing your request. Please try again.');
return new JsonResponse(['success' => false], 500);
}
}
#[Route('/examples/real-world', name: 'app_real_world')]
public function realWorldExamples(): Response
{
return $this->render('examples/real-world.html.twig');
}
#[Route('/examples/real-world/user-registration', name: 'app_example_user_registration')]
public function userRegistration(Request $request): Response
{
if ($request->isMethod('POST')) {
// Simulate user registration
$username = $request->request->get('username');
if (!empty($username)) {
flash()->option('timeout', 5000)
->option('showProgressbar', true)
->success('Registration successful! Welcome, ' . $username . '!');
// Send additional notification about email verification
flash()->option('timeout', 8000)
->info('A verification email has been sent to your inbox.');
} else {
flash()->error('Username is required.');
}
}
return $this->render('examples/user-registration.html.twig');
}
#[Route('/examples/real-world/shopping-cart', name: 'app_example_shopping_cart')]
public function shoppingCart(): Response
{
return $this->render('examples/shopping-cart.html.twig');
}
#[Route('/examples/real-world/add-to-cart', name: 'app_add_to_cart', methods: ['POST'])]
public function addToCart(Request $request): JsonResponse
{
if (!$request->isXmlHttpRequest()) {
return new JsonResponse(['error' => 'AJAX requests only'], 400);
}
$productId = $request->request->get('product_id');
$productName = $request->request->get('product_name', 'Product');
// Simulate adding to cart
flash()->option('position', 'bottom-right')
->option('showCloseButton', true)
->success($productName . ' added to your cart!');
return new JsonResponse(['success' => true]);
}
}
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class FeaturesController extends AbstractController
{
#[Route('/features', name: 'app_features')]
public function index(): Response
{
// Demonstrate a few different notification types
flash()->success('Explore the features of PHPFlasher!');
flash()->option('timeout', 8000)
->info('Click on the links to see each feature in action.');
return $this->render('features/index.html.twig');
}
#[Route('/features/types', name: 'app_types')]
public function types(): Response
{
// Demonstrate different notification types
flash()->success('This is a success notification!');
flash()->info('This is an info notification!');
flash()->warning('This is a warning notification!');
flash()->error('This is an error notification!');
return $this->render('features/types.html.twig');
}
#[Route('/features/positions', name: 'app_positions')]
public function positions(): Response
{
// Notifications with different positions
flash()->option('position', 'top-right')->success('This notification appears in the top-right corner!');
return $this->render('features/positions.html.twig');
}
#[Route('/features/options', name: 'app_options')]
public function options(): Response
{
// Notification with custom options
flash()->option('timeout', 8000)
->option('position', 'bottom-center')
->option('showProgressbar', true)
->success('This notification has custom options!');
return $this->render('features/options.html.twig');
}
#[Route('/features/plugins', name: 'app_plugins')]
public function plugins(): Response
{
// Plugin examples
flash()->addSuccess('This shows how PHPFlasher can be extended with plugins!');
return $this->render('features/plugins.html.twig');
}
#[Route('/features/presets', name: 'app_presets')]
public function presets(): Response
{
// Example of using presets
flash()->preset('profile.updated')
->success('Your profile has been updated successfully!');
flash()->preset('item.deleted')
->info('The item has been deleted.');
return $this->render('features/presets.html.twig');
}
}
+63 -70
View File
@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
@@ -13,77 +15,68 @@ class HomeController extends AbstractController
#[Route('/', name: 'app_home')]
public function index(): Response
{
$themes = [
// 'flasher',
// 'amber',
// 'sapphire',
// 'crystal',
// 'emerald',
// 'ruby',
// 'onyx',
// 'jade',
// 'aurora',
// 'neon',
// 'minimal',
// 'material',
// 'google',
// 'ios',
// 'slack',
// 'facebook',
'amazon',
];
$positions = [
// 'top-left',
'top-right',
// 'bottom-left',
// 'bottom-right',
// 'top-center',
// 'bottom-center',
// 'center-left',
// 'center-right',
// 'center-center',
];
$messages = [
'info' => 'Welcome back!',
'warning' => 'Are you sure you want to proceed?',
'error' => 'Oops! Something went wrong!',
'success' => 'Data has been saved successfully!',
];
foreach ($themes as $index => $theme) {
foreach ($messages as $type => $message) {
$position = $positions[$index % \count($positions)];
// $message = \sprintf('%s: %s', $theme, $message);
flash()
->use("theme.$theme")
->option('position', $position)
->$type($message);
}
}
$plugins = [
// 'noty',
// 'notyf',
// 'sweetalert',
// 'toastr',
];
foreach ($plugins as $plugin) {
foreach ($messages as $type => $message) {
flash()
->use($plugin)
->$type($message);
}
}
// Welcome notification
flash()->option('timeout', 10000)
->info('Welcome to the PHPFlasher Demo! 👋 Explore different features using the navigation.');
return $this->render('home/index.html.twig');
}
#[Route('/installation', name: 'app_installation')]
public function installation(): Response
{
return $this->render('home/installation.html.twig');
}
#[Route('/basic-usage', name: 'app_basic_usage')]
public function basicUsage(): Response
{
// Basic success notification
flash()->success('This is a success notification!');
return $this->render('home/basic-usage.html.twig');
}
#[Route('/playground', name: 'app_playground')]
public function playground(): Response
{
return $this->render('home/playground.html.twig');
}
#[Route('/theme-builder', name: 'app_theme_builder')]
public function themeBuilder(): Response
{
return $this->render('home/theme-builder.html.twig');
}
#[Route('/show-notification', name: 'app_show_notification', methods: ['POST'])]
public function showNotification(Request $request): JsonResponse
{
if (!$request->isXmlHttpRequest()) {
return new JsonResponse(['error' => 'AJAX requests only'], 400);
}
$data = json_decode($request->getContent(), true);
$type = $data['type'] ?? 'info';
$message = $data['message'] ?? 'This is a notification!';
// Create notification based on type
switch ($type) {
case 'success':
flash()->success($message);
break;
case 'error':
flash()->error($message);
break;
case 'warning':
flash()->warning($message);
break;
case 'info':
default:
flash()->info($message);
break;
}
return new JsonResponse(['status' => 'success']);
}
}
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class IntegrationController extends AbstractController
{
#[Route('/integration/api', name: 'app_integration_api')]
public function apiIntegration(): Response
{
// Example of how PHPFlasher can work with API responses
flash()->option('position', 'bottom-center')
->success('API request processed successfully!');
return $this->render('integration/api.html.twig');
}
#[Route('/integration/translation', name: 'app_integration_translation')]
public function translationIntegration(): Response
{
// Example using translation integration
flash()->option('translate', true)
->success('messages.success.operation_completed');
return $this->render('integration/translation.html.twig');
}
#[Route('/integration/symfony-flashbag', name: 'app_integration_flashbag')]
public function symfonyFlashbag(): Response
{
// Add message to Symfony flash bag to demonstrate integration
$this->addFlash('success', 'This message was added using Symfony\'s addFlash method!');
$this->addFlash('error', 'This error was added using Symfony\'s addFlash method!');
$this->addFlash('warning', 'This warning was added using Symfony\'s addFlash method!');
return $this->render('integration/flashbag.html.twig');
}
#[Route('/integration/custom-templates', name: 'app_integration_templates')]
public function customTemplates(): Response
{
// Example using custom templates
flash()->option('template', 'custom_template')
->success('This notification uses a custom template!');
return $this->render('integration/custom-templates.html.twig');
}
}
@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ThemesController extends AbstractController
{
#[Route('/themes', name: 'app_themes')]
public function index(): Response
{
// Show default theme notification
flash()->success('This is a notification with the default theme!');
return $this->render('themes/index.html.twig');
}
#[Route('/themes/flasher', name: 'app_theme_flasher')]
public function flasherTheme(): Response
{
// Using the Flasher theme
flash()->option('theme', 'flasher')
->success('This notification uses the Flasher theme!');
return $this->render('themes/flasher.html.twig');
}
#[Route('/themes/crystal', name: 'app_theme_crystal')]
public function crystalTheme(): Response
{
// Using the Crystal theme
flash()->option('theme', 'crystal')
->success('This notification uses the Crystal theme!');
return $this->render('themes/crystal.html.twig');
}
#[Route('/themes/emerald', name: 'app_theme_emerald')]
public function emeraldTheme(): Response
{
// Using the Emerald theme
flash()->option('theme', 'emerald')
->success('This notification uses the Emerald theme!');
return $this->render('themes/emerald.html.twig');
}
#[Route('/themes/sapphire', name: 'app_theme_sapphire')]
public function sapphireTheme(): Response
{
// Using the Sapphire theme
flash()->option('theme', 'sapphire')
->success('This notification uses the Sapphire theme!');
return $this->render('themes/sapphire.html.twig');
}
#[Route('/themes/amazon', name: 'app_theme_amazon')]
public function amazonTheme(): Response
{
// Using the Amazon theme
flash()->option('theme', 'amazon')
->success('This notification uses the Amazon theme!');
return $this->render('themes/amazon.html.twig');
}
}
+227 -13
View File
@@ -1,17 +1,231 @@
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Welcome!{% endblock %}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}PHPFlasher Demo{% endblock %}</title>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{# Tailwind CSS v4 CDN #}
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
{% block stylesheets %}{% endblock %}
{% block javascripts %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
{# Prism.js for syntax highlighting #}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.css">
{# Custom styles using Tailwind #}
<script type="text/tailwindcss">
@layer components {
.btn {
@apply px-4 py-2 rounded-md font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-opacity-50;
}
.btn-primary {
@apply bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-500;
}
.btn-success {
@apply bg-emerald-600 text-white hover:bg-emerald-700 focus:ring-emerald-500;
}
.btn-danger {
@apply bg-rose-600 text-white hover:bg-rose-700 focus:ring-rose-500;
}
.btn-warning {
@apply bg-amber-500 text-white hover:bg-amber-600 focus:ring-amber-400;
}
.btn-info {
@apply bg-sky-500 text-white hover:bg-sky-600 focus:ring-sky-400;
}
.btn-outline {
@apply border border-gray-300 text-gray-700 bg-white hover:bg-gray-50 focus:ring-indigo-500;
}
.code-block {
@apply rounded-lg overflow-hidden shadow-lg my-6;
}
.code-header {
@apply bg-gray-800 text-gray-200 px-4 py-2 flex justify-between items-center;
}
.theme-card {
@apply rounded-lg overflow-hidden bg-white shadow-md hover:shadow-lg transition duration-300;
}
.theme-preview {
@apply h-32 flex items-center justify-center;
}
}
</script>
{% block stylesheets %}{% endblock %}
</head>
<body class="bg-gray-50 antialiased text-gray-900 min-h-screen flex flex-col">
<header class="bg-indigo-600 text-white shadow-md">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center py-4">
<div class="flex items-center space-x-3">
<div class="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="ml-2 text-2xl font-bold">PHPFlasher</span>
</div>
<span class="hidden md:inline text-indigo-200">Beautiful Notifications for Your PHP Applications</span>
</div>
<div class="hidden md:flex space-x-4">
<a href="https://github.com/php-flasher/flasher" class="text-indigo-100 hover:text-white transition flex items-center" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
GitHub
</a>
<a href="https://php-flasher.io" class="text-indigo-100 hover:text-white transition flex items-center" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
</svg>
Documentation
</a>
</div>
<button id="mobile-menu-button" class="md:hidden text-white">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
</header>
<div id="mobile-menu" class="hidden md:hidden bg-indigo-700 text-white">
<div class="px-4 pt-2 pb-4 space-y-3">
<a href="{{ path('app_home') }}" class="block py-2 hover:bg-indigo-600 rounded px-2">Home</a>
<a href="{{ path('app_features') }}" class="block py-2 hover:bg-indigo-600 rounded px-2">Features</a>
<a href="{{ path('app_themes') }}" class="block py-2 hover:bg-indigo-600 rounded px-2">Themes</a>
<a href="{{ path('app_examples') }}" class="block py-2 hover:bg-indigo-600 rounded px-2">Examples</a>
<a href="{{ path('app_playground') }}" class="block py-2 hover:bg-indigo-600 rounded px-2">Playground</a>
<a href="https://github.com/php-flasher/flasher" class="block py-2 hover:bg-indigo-600 rounded px-2">GitHub</a>
<a href="https://php-flasher.io" class="block py-2 hover:bg-indigo-600 rounded px-2">Documentation</a>
</div>
</div>
<div class="flex flex-1">
<aside class="hidden md:block bg-white w-64 border-r border-gray-200 shadow-sm">
<div class="p-6 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-800">Navigation</h2>
<p class="text-sm text-gray-500 mt-1">Explore PHPFlasher</p>
</div>
<nav class="py-4">
<div class="px-6 py-2 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Getting Started
</div>
<a href="{{ path('app_home') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_home' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Home
</a>
<a href="{{ path('app_installation') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_installation' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Installation
</a>
<a href="{{ path('app_basic_usage') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_basic_usage' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Basic Usage
</a>
<div class="px-6 py-2 mt-4 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Features
</div>
<a href="{{ path('app_types') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_types' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Notification Types
</a>
<a href="{{ path('app_positions') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_positions' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Positions
</a>
<a href="{{ path('app_themes') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_themes' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Themes
</a>
<a href="{{ path('app_options') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_options' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Options & Configuration
</a>
<a href="{{ path('app_plugins') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_plugins' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Plugins
</a>
<div class="px-6 py-2 mt-4 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Examples
</div>
<a href="{{ path('app_form_example') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_form_example' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Form Validation
</a>
<a href="{{ path('app_ajax_example') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_ajax_example' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
AJAX Requests
</a>
<a href="{{ path('app_presets') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_presets' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Presets
</a>
<a href="{{ path('app_real_world') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_real_world' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Real-World Examples
</a>
<div class="px-6 py-2 mt-4 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Interactive
</div>
<a href="{{ path('app_playground') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_playground' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Interactive Playground
</a>
<a href="{{ path('app_theme_builder') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_theme_builder' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Theme Builder
</a>
</nav>
</aside>
<main class="flex-1 overflow-x-hidden">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="mb-6">
<h1 class="text-3xl font-bold text-gray-900">{% block page_title %}Welcome to PHPFlasher{% endblock %}</h1>
<p class="mt-2 text-lg text-gray-600">{% block page_subtitle %}A powerful notification system for PHP applications{% endblock %}</p>
</div>
{% block content %}{% endblock %}
</div>
</main>
</div>
<footer class="bg-white border-t border-gray-200 mt-auto">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
<div class="text-center md:text-left">
<p class="text-gray-500">
&copy; {{ "now"|date("Y") }} PHPFlasher. Made with ❤️ by <a href="https://github.com/yoeunes" class="text-indigo-600 hover:text-indigo-500">Younes ENNAJI</a>
</p>
</div>
<div class="flex space-x-6">
<a href="https://github.com/php-flasher/flasher" class="text-gray-500 hover:text-gray-700">
<span class="sr-only">GitHub</span>
<svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"></path>
</svg>
</a>
<a href="https://php-flasher.io" class="text-gray-500 hover:text-gray-700">
<span class="sr-only">Website</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</a>
</div>
</div>
</div>
</footer>
{# Prism.js for syntax highlighting #}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
{# Mobile menu toggle script #}
<script>
document.addEventListener('DOMContentLoaded', function() {
const mobileMenuButton = document.getElementById('mobile-menu-button');
const mobileMenu = document.getElementById('mobile-menu');
if (mobileMenuButton && mobileMenu) {
mobileMenuButton.addEventListener('click', function() {
mobileMenu.classList.toggle('hidden');
});
}
});
</script>
{% block javascripts %}{% endblock %}
</body>
</html>
@@ -0,0 +1,232 @@
{% extends 'base.html.twig' %}
{% block title %}PHPFlasher - Notification Positions{% endblock %}
{% block page_title %}Notification Positions{% endblock %}
{% block page_subtitle %}Control where your notifications appear on the screen{% endblock %}
{% block content %}
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Available Positions</h2>
<p class="text-gray-600 mb-6">
PHPFlasher allows you to position your notifications in different areas of the screen.
You can choose from 9 different positions to best fit your design and user experience needs.
</p>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-2 left-2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">top-left</div>
<div class="absolute top-2 right-2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="top-left">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-2 left-1/2 transform -translate-x-1/2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">top-center</div>
<div class="absolute top-2 left-1/2 transform -translate-x-1/2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="top-center">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-2 right-2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">top-right</div>
<div class="absolute top-2 right-2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="top-right">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-1/2 left-2 transform -translate-y-1/2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">center-left</div>
<div class="absolute top-1/2 left-2 transform -translate-y-1/2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="center-left">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">center</div>
<div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="center">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-1/2 right-2 transform -translate-y-1/2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">center-right</div>
<div class="absolute top-1/2 right-2 transform -translate-y-1/2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="center-right">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute bottom-2 left-2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">bottom-left</div>
<div class="absolute bottom-10 left-2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="bottom-left">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute bottom-2 left-1/2 transform -translate-x-1/2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">bottom-center</div>
<div class="absolute bottom-10 left-1/2 transform -translate-x-1/2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="bottom-center">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute bottom-2 right-2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">bottom-right</div>
<div class="absolute bottom-10 right-2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="bottom-right">
Show Notification
</button>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Setting Positions in Code</h2>
<p class="text-gray-600 mb-4">
You can set the position of notifications in your PHP code using the <code>option()</code> method:
</p>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">// Top-right position (default)
flash()->success('Your changes have been saved.');
// Bottom-left position
flash()->option('position', 'bottom-left')
->success('Your changes have been saved.');
// Top-center position
flash()->option('position', 'top-center')
->info('You have 3 unread messages.');
// Center position (middle of the screen)
flash()->option('position', 'center')
->warning('Your session will expire in 1 minute.');</code></pre>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Global Position Configuration</h2>
<p class="text-gray-600 mb-4">
You can set a default position for all notifications in your configuration file:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>YAML</span>
</div>
<pre><code class="language-yaml"># config/packages/flasher.yaml
flasher:
options:
position: 'top-right' # Default position for all notifications</code></pre>
</div>
<p class="text-gray-600 mb-4">
Or you can set specific positions for different themes:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>YAML</span>
</div>
<pre><code class="language-yaml"># config/packages/flasher.yaml
flasher:
themes:
amazon:
options:
position: 'bottom-right'
sapphire:
options:
position: 'top-center'</code></pre>
</div>
<div class="flex justify-center">
<a href="{{ path('app_themes') }}" class="btn btn-primary">
Explore Themes
</a>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const positionButtons = document.querySelectorAll('.notification-position');
positionButtons.forEach(button => {
button.addEventListener('click', function() {
const position = this.getAttribute('data-position');
// Send AJAX request to show notification
fetch('{{ path('app_show_notification') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
type: 'success',
message: 'This notification is at the ' + position + ' position!',
options: {
position: position
}
})
});
});
});
});
</script>
{% endblock %}
@@ -0,0 +1,220 @@
{% extends 'base.html.twig' %}
{% block title %}PHPFlasher - Notification Types{% endblock %}
{% block page_title %}Notification Types{% endblock %}
{% block page_subtitle %}Display different notification types to communicate various messages{% endblock %}
{% block content %}
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Available Notification Types</h2>
<p class="text-gray-600 mb-6">
PHPFlasher provides four primary notification types to communicate different types of messages to your users.
Each type has a distinct visual style to clearly convey the nature of the message.
</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
<div class="flex items-center mb-3">
<div class="bg-emerald-100 p-2 rounded-md mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
<h3 class="text-lg font-semibold">Success</h3>
</div>
<p class="text-gray-600 mb-3">
Used for positive feedback when an action has been completed successfully.
</p>
<div class="code-block mb-3">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">flash()->success('Your profile has been updated successfully!');</code></pre>
</div>
<button class="btn btn-success w-full notification-demo" data-type="success" data-message="This is a success notification!">
Show Success Notification
</button>
</div>
<div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
<div class="flex items-center mb-3">
<div class="bg-rose-100 p-2 rounded-md mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-rose-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<h3 class="text-lg font-semibold">Error</h3>
</div>
<p class="text-gray-600 mb-3">
Used to alert users about errors or problems that need attention.
</p>
<div class="code-block mb-3">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">flash()->error('An error occurred while processing your request.');</code></pre>
</div>
<button class="btn btn-danger w-full notification-demo" data-type="error" data-message="This is an error notification!">
Show Error Notification
</button>
</div>
<div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
<div class="flex items-center mb-3">
<div class="bg-amber-100 p-2 rounded-md mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-amber-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<h3 class="text-lg font-semibold">Warning</h3>
</div>
<p class="text-gray-600 mb-3">
Used to inform users about potential issues or actions they should be aware of.
</p>
<div class="code-block mb-3">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">flash()->warning('Your subscription will expire in 3 days.');</code></pre>
</div>
<button class="btn btn-warning w-full notification-demo" data-type="warning" data-message="This is a warning notification!">
Show Warning Notification
</button>
</div>
<div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
<div class="flex items-center mb-3">
<div class="bg-sky-100 p-2 rounded-md mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-sky-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h3 class="text-lg font-semibold">Info</h3>
</div>
<p class="text-gray-600 mb-3">
Used to provide neutral information or updates to users.
</p>
<div class="code-block mb-3">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">flash()->info('You have 5 unread messages.');</code></pre>
</div>
<button class="btn btn-info w-full notification-demo" data-type="info" data-message="This is an info notification!">
Show Info Notification
</button>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Usage in Controllers</h2>
<p class="text-gray-600 mb-4">
Here's how you can use notification types in your Symfony controllers:
</p>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php"><?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ExampleController extends AbstractController
{
#[Route('/process-form', name: 'app_process_form')]
public function processForm(): Response
{
// Simulate form processing
$success = true;
if ($success) {
// Show success notification
flash()->success('Form submitted successfully!');
} else {
// Show error notification
flash()->error('An error occurred while processing the form.');
}
// Show info notification
flash()->info('You will receive a confirmation email shortly.');
// Show warning notification
flash()->warning('Please verify your email within 24 hours.');
return $this->redirectToRoute('app_home');
}
}</code></pre>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Advanced Usage</h2>
<p class="text-gray-600 mb-4">
You can combine notification types with other options to create more customized notifications:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">// Success notification with custom timeout
flash()->option('timeout', 8000)
->success('Changes saved successfully!');
// Warning notification with custom position
flash()->option('position', 'bottom-center')
->warning('Your session will expire soon.');
// Error notification with progress bar
flash()->option('showProgressbar', true)
->error('Unable to connect to the server.');</code></pre>
</div>
<div class="flex justify-center">
<a href="{{ path('app_options') }}" class="btn btn-primary">
Learn More About Options
</a>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const demoButtons = document.querySelectorAll('.notification-demo');
demoButtons.forEach(button => {
button.addEventListener('click', function() {
const type = this.getAttribute('data-type');
const message = this.getAttribute('data-message') || 'This is a notification!';
// Send AJAX request to show notification
fetch('{{ path('app_show_notification') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
type: type,
message: message
})
});
});
});
});
</script>
{% endblock %}
+92 -2
View File
@@ -1,7 +1,97 @@
{% extends 'base.html.twig' %}
{% block title %}PHPFlasher{% endblock %}
{% block title %}PHPFlasher Features{% endblock %}
{% block body %}
{% block page_title %}PHPFlasher Features{% endblock %}
{% block page_subtitle %}Explore the powerful features of PHPFlasher{% endblock %}
{% block content %}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
<div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition duration-300">
<div class="bg-indigo-600 h-2"></div>
<div class="p-6">
<div class="flex items-center space-x-3 mb-3">
<div class="bg-indigo-100 p-2 rounded-md">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Notification Types</h3>
</div>
<p class="text-gray-600">Display success, error, warning, and info notifications to communicate different messages to your users.</p>
<div class="mt-4">
<a href="{{ path('app_types') }}" class="text-indigo-600 hover:text-indigo-800 font-medium">See Notification Types →</a>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition duration-300">
<div class="bg-emerald-600 h-2"></div>
<div class="p-6">
<div class="flex items-center space-x-3 mb-3">
<div class="bg-emerald-100 p-2 rounded-md">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4" />
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Notification Positions</h3>
</div>
<p class="text-gray-600">Place notifications in different positions around the screen to best suit your UI design.</p>
<div class="mt-4">
<a href="{{ path('app_positions') }}" class="text-emerald-600 hover:text-emerald-800 font-medium">Explore Positions →</a>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition duration-300">
<div class="bg-amber-500 h-2"></div>
<div class="p-6">
<div class="flex items-center space-x-3 mb-3">
<div class="bg-amber-100 p-2 rounded-md">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-amber-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Configuration Options</h3>
</div>
<p class="text-gray-600">Customize timeouts, animations, progressbars, and more with PHPFlasher's flexible configuration.</p>
<div class="mt-4">
<a href="{{ path('app_options') }}" class="text-amber-600 hover:text-amber-800 font-medium">View Options →</a>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<h3 class="text-xl font-bold mb-4 text-gray-800">Plugins & Extensions</h3>
<p class="text-gray-600 mb-4">PHPFlasher can be extended with plugins to add more functionality.</p>
<div class="flex justify-end">
<a href="{{ path('app_plugins') }}" class="btn btn-primary">Explore Plugins</a>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<h3 class="text-xl font-bold mb-4 text-gray-800">Notification Presets</h3>
<p class="text-gray-600 mb-4">Create reusable notification configurations for consistent messaging.</p>
<div class="flex justify-end">
<a href="{{ path('app_presets') }}" class="btn btn-primary">View Presets</a>
</div>
</div>
</div>
</div>
<div class="bg-indigo-50 rounded-lg p-8 mb-12">
<div class="text-center">
<h2 class="text-2xl font-bold text-indigo-800 mb-4">Try Out the Interactive Playground</h2>
<p class="text-indigo-600 mb-6 max-w-2xl mx-auto">Want to experiment with different notification settings? Use our interactive playground to test PHPFlasher's features in real-time.</p>
<div class="flex justify-center">
<a href="{{ path('app_playground') }}" class="btn btn-primary">Go to Playground</a>
</div>
</div>
</div>
{% endblock %}
@@ -0,0 +1,302 @@
{% extends 'base.html.twig' %}
{% block title %}PHPFlasher - Themes{% endblock %}
{% block page_title %}PHPFlasher Themes{% endblock %}
{% block page_subtitle %}Beautiful notification themes to match your application's design{% endblock %}
{% block content %}
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Available Themes</h2>
<p class="text-gray-600 mb-6">
PHPFlasher comes with various themes to help you match the notification style to your application's design.
Each theme provides a distinct visual appearance while maintaining the same functionality.
</p>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
<div class="theme-card">
<div class="bg-indigo-600 h-2"></div>
<div class="theme-preview bg-indigo-50 flex items-center justify-center p-4">
<div class="w-full bg-white rounded-lg shadow-md p-4">
<div class="flex">
<div class="flex-shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-indigo-500" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Flasher Default Theme</p>
<p class="text-xs text-gray-500">Clean and minimal design</p>
</div>
</div>
</div>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold mb-2">Flasher (Default)</h3>
<p class="text-gray-600 text-sm mb-4">The default theme with a clean, minimal design that works well in most applications.</p>
<button class="w-full btn btn-primary theme-demo" data-theme="flasher">
Preview Theme
</button>
</div>
</div>
<div class="theme-card">
<div class="bg-emerald-600 h-2"></div>
<div class="theme-preview bg-emerald-50 flex items-center justify-center p-4">
<div class="w-full bg-white rounded-lg shadow-md p-4 border-l-4 border-emerald-500">
<div class="flex">
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Emerald Theme</p>
<p class="text-xs text-gray-500">Elegant left-border style</p>
</div>
</div>
</div>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold mb-2">Emerald</h3>
<p class="text-gray-600 text-sm mb-4">A sleek theme with colored left borders and subtle design elements.</p>
<button class="w-full btn btn-primary theme-demo" data-theme="emerald">
Preview Theme
</button>
</div>
</div>
<div class="theme-card">
<div class="bg-blue-600 h-2"></div>
<div class="theme-preview bg-blue-50 flex items-center justify-center p-4">
<div class="w-full bg-white rounded-lg shadow-md p-4">
<div class="flex">
<div class="flex-shrink-0 bg-blue-100 rounded-full p-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-blue-500" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2h-1V9a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Sapphire Theme</p>
<p class="text-xs text-gray-500">Modern with rounded icons</p>
</div>
</div>
</div>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold mb-2">Sapphire</h3>
<p class="text-gray-600 text-sm mb-4">A modern theme with rounded icons and smooth animations.</p>
<button class="w-full btn btn-primary theme-demo" data-theme="sapphire">
Preview Theme
</button>
</div>
</div>
<div class="theme-card">
<div class="bg-purple-600 h-2"></div>
<div class="theme-preview bg-purple-50 flex items-center justify-center p-4">
<div class="w-full bg-white rounded-lg shadow-md p-4 bg-opacity-80 backdrop-filter backdrop-blur-sm">
<div class="flex">
<div class="flex-shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-purple-500" viewBox="0 0 20 20" fill="currentColor">
<path d="M2 10.5a1.5 1.5 0 113 0v6a1.5 1.5 0 01-3 0v-6zM6 10.333v5.43a2 2 0 001.106 1.79l.05.025A4 4 0 008.943 18h5.416a2 2 0 001.962-1.608l1.2-6A2 2 0 0015.56 8H12V4a2 2 0 00-2-2 1 1 0 00-1 1v.667a4 4 0 01-.8 2.4L6.8 7.933a4 4 0 00-.8 2.4z" />
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Crystal Theme</p>
<p class="text-xs text-gray-500">Transparent with blur effects</p>
</div>
</div>
</div>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold mb-2">Crystal</h3>
<p class="text-gray-600 text-sm mb-4">A modern glass-like theme with transparency and blur effects.</p>
<button class="w-full btn btn-primary theme-demo" data-theme="crystal">
Preview Theme
</button>
</div>
</div>
<div class="theme-card">
<div class="bg-amber-500 h-2"></div>
<div class="theme-preview bg-amber-50 flex items-center justify-center p-4">
<div class="w-full bg-white rounded-lg shadow-md p-4">
<div class="flex">
<div class="flex-shrink-0 bg-amber-100 p-1 rounded">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-amber-500" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M18 3a1 1 0 00-1.447-.894L8.763 6H5a3 3 0 000 6h.28l1.771 5.316A1 1 0 008 18h1a1 1 0 001-1v-4.382l6.553 3.276A1 1 0 0018 15V3z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Amazon Theme</p>
<p class="text-xs text-gray-500">Inspired by Amazon's notification style</p>
</div>
</div>
</div>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold mb-2">Amazon</h3>
<p class="text-gray-600 text-sm mb-4">Inspired by Amazon's notification style with distinctive icons.</p>
<button class="w-full btn btn-primary theme-demo" data-theme="amazon">
Preview Theme
</button>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Using Themes in Your Code</h2>
<p class="text-gray-600 mb-4">
You can specify which theme to use for each notification using the <code>option()</code> method:
</p>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">// Using the default theme
flash()->success('Your profile has been updated successfully!');
// Using the Emerald theme
flash()->option('theme', 'emerald')
->success('Your profile has been updated successfully!');
// Using the Sapphire theme with additional options
flash()->option('theme', 'sapphire')
->option('position', 'bottom-right')
->option('timeout', 5000)
->info('You have 3 unread messages.');
// Using the Amazon theme
flash()->option('theme', 'amazon')
->warning('Your subscription will expire in 3 days.');</code></pre>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Theme Configuration</h2>
<p class="text-gray-600 mb-4">
You can configure themes globally in your configuration file:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>YAML</span>
</div>
<pre><code class="language-yaml"># config/packages/flasher.yaml
flasher:
# Set the default theme for all notifications
default: theme.amazon
themes:
amazon:
scripts:
- '/vendor/flasher/themes/amazon/amazon.min.js'
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/amazon/amazon.min.css'
options:
position: 'bottom-right'
timeout: 6000
emerald:
scripts:
- '/vendor/flasher/themes/emerald/emerald.min.js'
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/emerald/emerald.min.css'
options:
position: 'top-right'
timeout: 5000</code></pre>
</div>
<p class="text-gray-600 mb-4">
Each theme can have its own default options, scripts, and styles. This allows you to create a consistent notification experience throughout your application.
</p>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Custom Theme Templates</h2>
<p class="text-gray-600 mb-4">
Need something more customized? PHPFlasher allows you to create your own themes:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>YAML</span>
</div>
<pre><code class="language-yaml"># config/packages/flasher.yaml
flasher:
themes:
custom_theme:
scripts:
- '/assets/js/custom-theme.js'
styles:
- '/assets/css/custom-theme.css'
options:
position: 'top-center'
timeout: 7000
# Add any custom options your theme supports</code></pre>
</div>
<p class="text-gray-600 mb-4">
Then you can use your custom theme in your code:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">flash()->option('theme', 'custom_theme')
->success('This uses your custom theme!');</code></pre>
</div>
<div class="flex justify-center">
<a href="{{ path('app_theme_builder') }}" class="btn btn-primary">
Try the Theme Builder
</a>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const themeButtons = document.querySelectorAll('.theme-demo');
themeButtons.forEach(button => {
button.addEventListener('click', function() {
const theme = this.getAttribute('data-theme');
const messages = {
'flasher': 'This notification uses the Flasher theme!',
'emerald': 'This notification uses the Emerald theme!',
'sapphire': 'This notification uses the Sapphire theme!',
'crystal': 'This notification uses the Crystal theme!',
'amazon': 'This notification uses the Amazon theme!'
};
// Send AJAX request to show notification
fetch('{{ path('app_show_notification') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
type: 'success',
message: messages[theme] || 'Theme preview',
options: {
theme: theme
}
})
});
});
});
});
</script>
{% endblock %}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"dist/main.css": "/dist/main.7cce6945.css",
"dist/main.css": "/dist/main.2e8d1953.css",
"dist/main.js": "/dist/main.8b056786.js",
"dist/455.3a7b4474.css": "/dist/455.3a7b4474.css",
"dist/455.095e6545.js": "/dist/455.095e6545.js",
+1 -1
View File
@@ -1,5 +1,5 @@
<!-- PHPFlasher Interactive Studio - Enhanced 2.0 -->
<section data-controller="flasher-studio" class="relative overflow-hidden bg-gradient-to-br from-indigo-50 to-slate-50 py-12 my-8 rounded-2xl">
<section data-controller="flasher-studio" class="relative overflow-hidden bg-gradient-to-br from-indigo-50 to-slate-50 py-12 my-8 rounded-2xl max-h-screen">
<!-- Dynamic background elements -->
<div class="absolute inset-0 z-0">
<div class="absolute top-0 right-0 w-1/3 h-1/3 bg-gradient-to-br from-indigo-500/10 to-purple-500/10 rounded-full blur-3xl animate-[float_20s_ease-in-out_infinite]"></div>
+1 -1
View File
@@ -1,4 +1,4 @@
<header class="sticky top-0 z-30 w-full bg-white border-b border-slate-200 shadow-sm">
<header class="w-full bg-white border-b border-slate-200 shadow-sm">
<div class="container max-w-7xl mx-auto px-4 lg:px-6">
<div class="flex items-center justify-between h-16">
<!-- Logo Area -->
+1 -1
View File
@@ -9,7 +9,7 @@
{% include header.html %}
<div class="container max-w-7xl mx-auto px-4 lg:px-6 py-8">
<div class="container mx-auto px-4 lg:px-6 py-8">
<div class="flex flex-col lg:flex-row gap-8">
<!-- Sidebar Navigation -->
<aside class="lg:w-64 shrink-0">
-757
View File
@@ -1,757 +0,0 @@
<!DOCTYPE html>
<html lang="en" class="scroll-smooth">
<head>
{% include head.html %}
<link rel="stylesheet" href="/static/css/home.css">
</head>
<body class="min-h-screen bg-slate-50 text-slate-800 font-sans leading-relaxed" data-controller="anchor clipboard navigation tryit">
{% include size-helper.html %}
{% include banner.html %}
{% include header.html %}
<div class="container max-w-7xl mx-auto px-4 lg:px-6 py-4">
<!-- Hero Section with Animated Bell in SVG -->
<section class="relative overflow-hidden bg-gradient-to-b from-indigo-50 to-white pt-16 pb-12 rounded-3xl mb-16 shadow-sm">
<div class="absolute inset-0 overflow-hidden">
<div class="absolute left-0 top-0 w-1/3 h-1/3 bg-gradient-to-br from-purple-100 to-transparent opacity-60 blur-3xl"></div>
<div class="absolute right-0 bottom-0 w-1/3 h-1/3 bg-gradient-to-tl from-indigo-100 to-transparent opacity-60 blur-3xl"></div>
</div>
<div class="relative z-10 container mx-auto px-4">
<div class="text-center">
<div class="flex justify-center mb-8 animate-fade-in">
<div class="relative inline-block">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="320" height="50" viewBox="0 0 5082 781" preserveAspectRatio="xMidYMid meet" class="mx-auto">
<g transform="translate(0.000000,781.000000) scale(0.100000,-0.100000)" fill="#FFFFFF" stroke="none">
<!-- Bell path with animation class -->
<path id="bell-icon" class="animate-bell" fill="#4052B5" d="M45570 7540 c-283 -43 -490 -282 -490 -565 0 -200 118 -404 284 -491 31 -16 55 -32 53 -35 -1 -4 -39 -34 -85 -68 -439 -325 -739 -810 -843 -1365 -27 -142 -37 -465 -19 -617 31 -272 65 -381 300 -954 259 -633 265 -653 265 -955 0 -149 -4 -215 -18 -280 -32 -153 -79 -282 -161 -440 -44 -85 -95 -184 -113 -220 -101 -198 -102 -473 -3 -671 115 -230 303 -375 550 -425 98 -19 249 -15 343 10 31 9 363 142 738 296 374 154 683 280 687 280 4 0 16 -30 26 -66 43 -151 158 -332 273 -432 263 -226 594 -301 920 -206 153 44 272 114 392 228 93 90 150 169 205 287 68 144 90 246 89 409 -1 162 -28 283 -96 422 l-42 88 45 19 c25 10 326 135 670 276 344 141 657 273 695 293 102 53 247 200 302 307 60 117 84 203 90 325 13 240 -73 452 -250 616 -94 88 -205 147 -372 198 -66 21 -151 49 -190 63 -322 119 -616 373 -784 677 -27 50 -136 300 -241 554 -206 500 -223 539 -308 676 -338 546 -876 904 -1521 1013 -145 25 -513 24 -653 0 -137 -24 -128 -29 -105 56 32 125 13 277 -51 402 -102 201 -356 329 -582 295z"/>
<path fill="#2D264B" d="M19237 6013 c-4 -3 -7 -1252 -7 -2775 l0 -2768 640 0 640 0 0 2775 0 2775 -633 0 c-349 0 -637 -3 -640 -7z"/>
<path fill="#2D264B" d="M30710 3245 l0 -2775 640 0 640 0 0 1188 c0 1271 1 1296 51 1444 43 129 97 218 188 308 131 131 259 188 464 210 75 8 131 8 199 1 345 -39 574 -235 670 -576 l23 -80 3 -1247 3 -1248 640 0 640 0 -4 1348 c-3 1322 -3 1349 -25 1479 -84 520 -288 873 -657 1138 -428 307 -1127 349 -1655 99 -185 -88 -377 -237 -503 -394 l-37 -45 0 963 0 962 -640 0 -640 0 0 -2775z"/>
<path fill="#4052B5" d="M300 3100 l0 -2630 635 0 635 0 0 944 0 944 538 5 c576 5 639 9 881 63 482 108 880 386 1100 769 100 174 176 412 201 627 18 149 8 429 -20 578 -79 425 -298 769 -636 996 -234 156 -489 250 -832 306 -135 22 -154 22 -1319 25 l-1183 4 0 -2631z m2239 1571 c223 -58 362 -183 427 -385 22 -66 27 -106 31 -216 6 -166 -10 -251 -68 -368 -71 -145 -189 -239 -369 -293 -72 -22 -92 -23 -532 -27 l-458 -3 0 654 c0 359 3 657 7 661 4 4 204 6 443 3 408 -3 440 -5 519 -26z"/>
<path fill="#4052B5" d="M4970 3100 l0 -2630 640 0 640 0 0 1085 0 1085 1000 0 1000 0 0 -1085 0 -1085 640 0 640 0 0 2630 0 2630 -640 0 -640 0 0 -1030 0 -1030 -1000 0 -1000 0 0 1030 0 1030 -640 0 -640 0 0 -2630z"/>
<path fill="#4052B5" d="M10460 3100 l0 -2630 635 0 635 0 0 944 0 944 538 5 c576 5 639 9 881 63 343 77 630 232 866 469 132 131 199 224 275 380 127 258 177 510 167 834 -8 275 -54 479 -158 700 -141 299 -362 524 -680 690 -181 95 -394 160 -657 203 -135 22 -154 22 -1319 25 l-1183 4 0 -2631z m2239 1571 c115 -30 199 -72 267 -134 126 -114 183 -252 191 -457 15 -357 -130 -578 -437 -671 -72 -22 -92 -23 -532 -27 l-458 -3 0 654 c0 359 3 657 7 661 4 4 204 6 443 3 408 -3 440 -5 519 -26z"/>
<path fill="#2D264B" d="M15130 3100 l0 -2630 640 0 640 0 0 1065 0 1065 805 0 805 0 0 495 0 495 -802 2 -803 3 0 555 0 555 1073 3 1072 2 0 510 0 510 -1715 0 -1715 0 0 -2630z"/>
<path fill="#2D264B" d="M22776 4695 c-704 -100 -1262 -603 -1481 -1335 -112 -373 -137 -881 -65 -1300 101 -589 393 -1067 829 -1361 433 -292 1009 -369 1511 -203 273 91 532 273 692 487 l58 79 0 -296 0 -296 640 0 640 0 0 2090 0 2090 -640 0 -640 0 -2 -291 -3 -291 -43 61 c-128 182 -349 356 -578 456 -95 41 -223 79 -341 101 -122 22 -447 28 -577 9z m854 -1126 c156 -39 317 -138 441 -274 176 -192 259 -459 246 -790 -12 -304 -95 -519 -270 -702 -135 -140 -281 -223 -456 -258 -128 -26 -337 -17 -451 20 -236 76 -406 216 -520 428 -80 147 -118 301 -127 512 -19 439 134 771 446 963 93 57 239 109 344 122 97 12 254 2 347 -21z"/>
<path fill="#2D264B" d="M27855 4699 c-696 -66 -1217 -427 -1369 -950 -36 -124 -50 -261 -42 -408 14 -287 94 -485 266 -661 242 -247 448 -339 1170 -520 456 -115 702 -208 774 -292 122 -142 63 -361 -120 -452 -85 -42 -175 -59 -319 -59 -100 1 -148 6 -208 22 -221 59 -386 215 -428 404 l-12 57 -629 0 -628 0 0 -36 c0 -57 37 -225 71 -321 72 -203 168 -353 334 -518 292 -293 685 -471 1185 -537 124 -16 538 -16 655 0 313 44 578 131 790 262 280 172 467 418 546 717 26 99 36 322 20 446 -41 309 -186 542 -467 753 -171 127 -521 256 -1079 395 -486 122 -635 188 -702 311 -26 48 -25 198 2 253 66 134 232 201 475 192 192 -7 316 -55 430 -167 71 -69 120 -161 144 -272 l6 -28 591 0 591 0 -7 53 c-22 167 -85 377 -155 517 -100 198 -285 414 -467 543 -225 160 -528 263 -867 296 -126 13 -419 12 -551 0z"/>
<path fill="#2D264B" d="M37390 4704 c-340 -40 -571 -103 -813 -225 -298 -150 -537 -361 -724 -639 -204 -305 -321 -686 -342 -1122 -28 -570 90 -1048 357 -1443 192 -285 415 -480 727 -635 228 -113 428 -173 705 -211 144 -20 518 -17 660 4 478 74 859 262 1161 574 164 169 302 371 382 559 38 89 97 259 97 281 0 11 -121 13 -679 13 l-680 0 -38 -72 c-77 -146 -231 -265 -399 -308 -322 -83 -667 18 -843 247 -92 119 -157 290 -177 462 l-7 61 1451 0 1450 0 9 78 c13 120 10 515 -5 627 -69 522 -310 986 -667 1284 -286 238 -615 382 -1020 447 -95 15 -521 28 -605 18z m423 -1040 c221 -46 420 -194 508 -378 39 -81 69 -202 69 -277 l0 -39 -800 0 c-770 0 -800 1 -800 18 0 47 45 186 89 277 115 236 326 380 610 415 82 11 234 3 324 -16z"/>
<path fill="#2D264B" d="M42860 4693 c-173 -12 -381 -68 -536 -144 -215 -105 -440 -292 -590 -492 l-74 -98 0 346 0 345 -640 0 -640 0 0 -2090 0 -2090 640 0 640 0 0 1029 c0 884 2 1047 16 1153 9 67 20 128 25 134 5 6 10 20 12 32 2 12 6 25 10 28 4 4 7 15 7 24 0 9 18 52 40 96 101 198 323 325 640 364 46 5 191 10 322 10 l238 0 0 680 0 680 -32 -2 c-18 -1 -53 -3 -78 -5z"/>
</g>
</svg>
</div>
</div>
<h1 class="text-3xl md:text-4xl font-bold text-slate-800 mb-4 tracking-tight">
Beautiful Flash Notifications <span class="text-indigo-600">Made Simple</span>
</h1>
<p class="text-lg text-slate-600 max-w-2xl mx-auto mb-8">
Add elegant notifications to your Laravel or Symfony applications with just <span class="bg-indigo-50 text-indigo-700 px-2 py-1 rounded">one line of code</span>. Perfect for developers of all skill levels.
</p>
<!--
<div class="flex flex-wrap gap-3 justify-center mb-8">
<a href="#quick-start" class="px-5 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors duration-200 shadow-md hover:shadow-lg flex items-center">
<i class="fa-solid fa-rocket mr-2"></i> Get Started
</a>
<a href="https://github.com/php-flasher/php-flasher" class="px-5 py-3 bg-white hover:bg-slate-50 text-slate-700 font-medium rounded-lg border border-slate-200 transition-colors duration-200 flex items-center">
<i class="fa-brands fa-github mr-2"></i> View on GitHub
</a>
</div>
-->
<div class="flex flex-wrap justify-center gap-3">
<a href="https://www.linkedin.com/in/younes--ennaji/" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/badge/author-@yoeunes-blue.svg" alt="Author Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://github.com/php-flasher/php-flasher" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/badge/source-php--flasher/php--flasher-blue.svg" alt="Source Code Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://github.com/php-flasher/php-flasher/releases" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/github/tag/php-flasher/flasher.svg" alt="GitHub Release Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://github.com/php-flasher/flasher/blob/master/LICENSE" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/badge/license-MIT-brightgreen.svg" alt="License Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://packagist.org/packages/php-flasher/flasher" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/packagist/dt/php-flasher/flasher.svg" alt="Packagist Downloads Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://github.com/php-flasher/php-flasher" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/github/stars/php-flasher/php-flasher.svg" alt="GitHub Stars Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://packagist.org/packages/php-flasher/flasher" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/packagist/php-v/php-flasher/flasher.svg" alt="Supported PHP Version Badge" class="transform hover:scale-105 transition-transform" />
</a>
</div>
</div>
</div>
</section>
{% include flasher-studio.html %}
<!-- Quick Start Section (Moved before Features) -->
<section id="quick-start" class="container mx-auto px-4 mb-16">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Get Started</div>
<h2 class="text-2xl md:text-3xl font-bold text-slate-800 mb-2">Quick Installation</h2>
<p class="text-slate-600 max-w-2xl mx-auto">Get up and running in less than a minute</p>
</div>
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-xl shadow-md overflow-hidden border border-slate-100 mb-8">
<div class="bg-slate-800 p-4 flex items-center justify-between">
<div class="flex items-center">
<div class="flex space-x-1 mr-4">
<div class="w-3 h-3 rounded-full bg-red-400"></div>
<div class="w-3 h-3 rounded-full bg-yellow-400"></div>
<div class="w-3 h-3 rounded-full bg-green-400"></div>
</div>
<span class="text-slate-400 text-sm">Terminal</span>
</div>
<div class="text-slate-400 text-sm">Installation</div>
</div>
<div class="p-6 bg-slate-900">
<div class="flex flex-col gap-4">
<div class="relative">
<div class="flex items-center mb-2">
<div class="w-5 h-5 bg-red-500 text-white rounded-full flex items-center justify-center mr-2 text-xs">
<i class="fa-brands fa-laravel"></i>
</div>
<span class="text-slate-300 text-sm">Laravel Installation</span>
</div>
<pre class="bg-slate-800 p-4 rounded-lg text-sm overflow-x-auto"><code class="language-bash text-slate-200">composer require php-flasher/flasher-laravel</code></pre>
</div>
<div class="relative">
<div class="flex items-center mb-2">
<div class="w-5 h-5 bg-black text-white rounded-full flex items-center justify-center mr-2 text-xs">
<i class="fa-brands fa-symfony"></i>
</div>
<span class="text-slate-300 text-sm">Symfony Installation</span>
</div>
<pre class="bg-slate-800 p-4 rounded-lg text-sm overflow-x-auto"><code class="language-bash text-slate-200">composer require php-flasher/flasher-symfony</code></pre>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Laravel Quick Start -->
<a href="/laravel/" class="group bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 overflow-hidden hover:border-red-200">
<div class="p-6">
<div class="flex items-center mb-4">
<div class="w-10 h-10 bg-red-50 text-red-500 rounded-lg flex items-center justify-center mr-3 group-hover:bg-red-100 transition-colors">
<i class="fa-brands fa-laravel text-lg"></i>
</div>
<h3 class="text-lg font-semibold text-slate-800">Laravel Guide</h3>
</div>
<p class="text-slate-600 mb-4">Perfect integration with Laravel's notification system. Get started in minutes.</p>
<div class="flex items-center text-red-600 font-medium">
Learn more
<i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
<!-- Symfony Quick Start -->
<a href="/symfony/" class="group bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 overflow-hidden hover:border-slate-300">
<div class="p-6">
<div class="flex items-center mb-4">
<div class="w-10 h-10 bg-slate-50 text-black rounded-lg flex items-center justify-center mr-3 group-hover:bg-slate-100 transition-colors">
<i class="fa-brands fa-symfony text-lg"></i>
</div>
<h3 class="text-lg font-semibold text-slate-800">Symfony Guide</h3>
</div>
<p class="text-slate-600 mb-4">Complete Symfony integration with bundle configuration and services.</p>
<div class="flex items-center text-slate-700 font-medium">
Learn more
<i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6">
<!-- Livewire Quick Start -->
<a href="/livewire/" class="group bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 overflow-hidden hover:border-purple-200">
<div class="p-6">
<div class="flex items-center mb-4">
<div class="w-10 h-10 bg-purple-50 text-purple-600 rounded-lg flex items-center justify-center mr-3 group-hover:bg-purple-100 transition-colors">
<i class="fa-solid fa-bolt text-lg"></i>
</div>
<h3 class="text-lg font-semibold text-slate-800">Livewire Guide</h3>
</div>
<p class="text-slate-600 mb-4">Real-time notifications in your Laravel Livewire components.</p>
<div class="flex items-center text-purple-600 font-medium">
Learn more
<i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
<!-- Inertia Quick Start -->
<a href="/inertia/" class="group bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 overflow-hidden hover:border-blue-200">
<div class="p-6">
<div class="flex items-center mb-4">
<div class="w-10 h-10 bg-blue-50 text-blue-600 rounded-lg flex items-center justify-center mr-3 group-hover:bg-blue-100 transition-colors">
<i class="fa-solid fa-arrows-rotate text-lg"></i>
</div>
<h3 class="text-lg font-semibold text-slate-800">Inertia Guide</h3>
</div>
<p class="text-slate-600 mb-4">Modern SPA notifications with Inertia.js integration.</p>
<div class="flex items-center text-blue-600 font-medium">
Learn more
<i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
</div>
</div>
</section>
<!-- Features Section -->
<section class="container mx-auto px-4 mb-20">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Features</div>
<h2 class="text-3xl sm:text-4xl font-bold text-slate-800 mb-3">Why Choose PHPFlasher?</h2>
<p class="text-slate-600 max-w-2xl mx-auto">Everything you need to add beautiful notifications to your PHP applications</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto">
<!-- Feature 1 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-indigo-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-wand-magic-sparkles text-indigo-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="easy-to-use">Easy to Use</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Simple one-line code to show notifications</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Works right out of the box - zero configuration</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Intuitive, consistent API across libraries</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Customize notifications with ease</span>
</li>
</ul>
</div>
<!-- Feature 2 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-purple-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-sliders text-purple-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="flexible">Flexible & Powerful</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Multiple notification libraries in one package</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Session and direct rendering options</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Customizable positions, animations, and styles</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Flash messages across redirects</span>
</li>
</ul>
</div>
<!-- Feature 3 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-blue-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-code text-blue-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="integration">Framework Integration</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-brands fa-laravel text-red-500 mt-1 mr-2.5"></i>
<span>Native Laravel integration with facades</span>
</li>
<li class="flex items-start">
<i class="fa-brands fa-symfony text-black mt-1 mr-2.5"></i>
<span>Seamless Symfony support with bundle</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-bolt text-purple-500 mt-1 mr-2.5"></i>
<span>Livewire real-time notifications</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-arrows-rotate text-blue-500 mt-1 mr-2.5"></i>
<span>Inertia.js SPA support</span>
</li>
</ul>
</div>
<!-- Feature 4 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-emerald-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-user-gear text-emerald-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="developer-friendly">Developer Experience</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Full IDE support with autocompletion</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Extensive documentation with examples</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>TypeScript support for JavaScript usage</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Well-tested, production-ready code</span>
</li>
</ul>
</div>
<!-- Feature 5 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-amber-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-rocket text-amber-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="performance">Performance Optimized</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Lightweight core with lazy-loading</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Only loads required JavaScript libraries</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Optimized asset loading strategies</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Minimal framework overhead</span>
</li>
</ul>
</div>
<!-- Feature 6 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-pink-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-palette text-pink-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="customizable">Highly Customizable</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Easily style notifications to match your brand</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Configure default settings globally</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Override options per notification</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Create custom themes and templates</span>
</li>
</ul>
</div>
</div>
</section>
<!-- Library Showcase Section -->
<section class="container mx-auto px-4 mb-20">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Libraries</div>
<h2 class="text-3xl sm:text-4xl font-bold text-slate-800 mb-3">Choose Your Preferred Library</h2>
<p class="text-slate-600 max-w-2xl mx-auto">PHPFlasher integrates with popular JavaScript notification libraries</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 max-w-5xl mx-auto">
<!-- Toastr -->
<a href="/library/toastr/" class="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 border border-slate-100 overflow-hidden flex flex-col h-full transform hover:-translate-y-1">
<div class="h-36 bg-gradient-to-br from-blue-400 to-blue-600 flex items-center justify-center relative overflow-hidden">
<div class="absolute inset-0 bg-blue-600 opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="w-20 h-20 bg-white rounded-full flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300">
<i class="fa-solid fa-message text-blue-500 text-3xl"></i>
</div>
</div>
<div class="p-6 flex-grow">
<h3 class="font-semibold text-lg text-slate-800 mb-2">Toastr.js</h3>
<p class="text-slate-600 text-sm mb-4">Simple toast notifications for web applications. Clean, elegant and customizable.</p>
<div class="flex items-center text-blue-600 font-medium text-sm mt-auto group-hover:underline">
Learn more <i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
<!-- SweetAlert2 -->
<a href="/library/sweetalert/" class="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 border border-slate-100 overflow-hidden flex flex-col h-full transform hover:-translate-y-1">
<div class="h-36 bg-gradient-to-br from-pink-400 to-red-500 flex items-center justify-center relative overflow-hidden">
<div class="absolute inset-0 bg-red-500 opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="w-20 h-20 bg-white rounded-full flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300">
<i class="fa-solid fa-circle-exclamation text-pink-500 text-3xl"></i>
</div>
</div>
<div class="p-6 flex-grow">
<h3 class="font-semibold text-lg text-slate-800 mb-2">SweetAlert2</h3>
<p class="text-slate-600 text-sm mb-4">Beautiful, responsive, customizable alert dialogs with interactive elements.</p>
<div class="flex items-center text-pink-600 font-medium text-sm mt-auto group-hover:underline">
Learn more <i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
<!-- Noty -->
<a href="/library/noty/" class="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 border border-slate-100 overflow-hidden flex flex-col h-full transform hover:-translate-y-1">
<div class="h-36 bg-gradient-to-br from-purple-400 to-indigo-600 flex items-center justify-center relative overflow-hidden">
<div class="absolute inset-0 bg-indigo-600 opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="w-20 h-20 bg-white rounded-full flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300">
<i class="fa-solid fa-bell text-purple-500 text-3xl"></i>
</div>
</div>
<div class="p-6 flex-grow">
<h3 class="font-semibold text-lg text-slate-800 mb-2">Noty</h3>
<p class="text-slate-600 text-sm mb-4">Highly customizable notification library with multiple positions and animations.</p>
<div class="flex items-center text-purple-600 font-medium text-sm mt-auto group-hover:underline">
Learn more <i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
<!-- Notyf -->
<a href="/library/notyf/" class="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 border border-slate-100 overflow-hidden flex flex-col h-full transform hover:-translate-y-1">
<div class="h-36 bg-gradient-to-br from-green-400 to-teal-500 flex items-center justify-center relative overflow-hidden">
<div class="absolute inset-0 bg-teal-500 opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="w-20 h-20 bg-white rounded-full flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300">
<i class="fa-solid fa-comment-dots text-green-500 text-3xl"></i>
</div>
</div>
<div class="p-6 flex-grow">
<h3 class="font-semibold text-lg text-slate-800 mb-2">Notyf</h3>
<p class="text-slate-600 text-sm mb-4">Minimalist toast notifications with a clean design and smooth animations.</p>
<div class="flex items-center text-green-600 font-medium text-sm mt-auto group-hover:underline">
Learn more <i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
</div>
<div class="text-center mt-10">
<a href="/library/toastr/" class="inline-flex items-center px-6 py-3 bg-white border border-slate-200 hover:bg-slate-50 text-slate-700 rounded-lg font-medium transition-colors duration-200 shadow-sm">
<i class="fa-solid fa-th-large mr-2"></i> Compare all libraries
</a>
</div>
</section>
<!-- Code Example Section -->
<section class="container mx-auto px-4 mb-16">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Code Examples</div>
<h2 class="text-2xl md:text-3xl font-bold text-slate-800 mb-2">Simple Implementation</h2>
<p class="text-slate-600 max-w-2xl mx-auto">Just a few lines of code to get started</p>
</div>
<div class="flex flex-col gap-8 max-w-4xl mx-auto">
<!-- PHP Example -->
<div class="bg-white rounded-xl shadow-md overflow-hidden border border-slate-100">
<div class="bg-slate-800 px-4 py-3 flex items-center">
<div class="flex space-x-1.5 mr-3">
<div class="w-2.5 h-2.5 rounded-full bg-red-500"></div>
<div class="w-2.5 h-2.5 rounded-full bg-yellow-500"></div>
<div class="w-2.5 h-2.5 rounded-full bg-green-500"></div>
</div>
<div class="text-white opacity-80 text-xs font-medium">Controller.php</div>
</div>
<div>
<pre class="bg-slate-50 rounded-lg p-4 text-sm overflow-x-auto"><code class="language-php">&lt;?php
namespace App\Http\Controllers;
class ProfileController
{
public function update()
{
// Update user profile logic
// Show success notification
flash()->success('Profile updated successfully!');
// Or add a title
flash()->success('Your changes have been saved.', 'Profile Updated');
// Or add a title and options
flash()
->option('timeout', 5000)
->option('position', 'top-right')
->success('Changes saved with custom settings');
// Change notification options
flash()->options([
'timeout' => 5000,
'position' => 'top-right'
])->success('Changes saved with custom settings');
return back();
}
}</code></pre>
</div>
</div>
<!-- JavaScript Example -->
<div class="bg-white rounded-xl shadow-md overflow-hidden border border-slate-100">
<div class="bg-slate-800 px-4 py-3 flex items-center">
<div class="flex space-x-1.5 mr-3">
<div class="w-2.5 h-2.5 rounded-full bg-red-500"></div>
<div class="w-2.5 h-2.5 rounded-full bg-yellow-500"></div>
<div class="w-2.5 h-2.5 rounded-full bg-green-500"></div>
</div>
<div class="text-white opacity-80 text-xs font-medium">app.js</div>
</div>
<div>
<pre class="bg-slate-50 rounded-lg p-4 text-sm overflow-x-auto"><code class="language-javascript">// Form submission example
document.getElementById('contact-form').addEventListener('submit', async function(e) {
e.preventDefault();
try {
const response = await submitForm(this);
if (response.success) {
// Show success notification
flasher.success('Your message has been sent!', 'Thank You');
} else {
// Show error notification
flasher.error('Please check your form inputs', 'Error');
}
} catch (error) {
// Show error notification with details
flasher.error('Server error, please try again later.', 'Error');
console.error(error);
}
});</code></pre>
</div>
</div>
</div>
</section>
<!-- Testimonials Section -->
<section class="container mx-auto px-4 mb-20">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Testimonials</div>
<h2 class="text-3xl sm:text-4xl font-bold text-slate-800 mb-3">Loved by Developers</h2>
<p class="text-slate-600 max-w-2xl mx-auto">See what others are saying about PHPFlasher</p>
</div>
<div class="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- Testimonial 1 -->
<div class="bg-white rounded-xl shadow-md p-6 border border-slate-100">
<div class="flex items-center mb-4">
<div class="text-amber-400 flex">
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
</div>
</div>
<blockquote class="text-slate-700 mb-6">
"Honestly wasn't expecting much, but wow! Saved me hours of headaches with our notification system. Just dropped it in and it worked first try. No more fighting with JS libraries!"
</blockquote>
<div class="flex items-center">
<img src="https://github.com/salmayno.png" alt="Salma Mourad" class="w-10 h-10 rounded-full mr-3 object-cover">
<div>
<div class="font-medium text-slate-800">Salma Mourad</div>
<div class="text-slate-500 text-sm">@salmayno · Freelancer</div>
</div>
</div>
</div>
<!-- Testimonial 2 -->
<div class="bg-white rounded-xl shadow-md p-6 border border-slate-100">
<div class="flex items-center mb-4">
<div class="text-amber-400 flex">
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star-half-stroke"></i>
</div>
</div>
<blockquote class="text-slate-700 mb-6">
"We were using Toastr directly but it was a mess maintaining it. Switched to PHPFlasher last sprint and it just works. The Livewire integration is a huge time saver too. Only nitpick is I wish the docs had more examples."
</blockquote>
<div class="flex items-center">
<img src="https://github.com/yae96.png" alt="Youness AIT EL HADJ" class="w-10 h-10 rounded-full mr-3 object-cover">
<div>
<div class="font-medium text-slate-800">Youness AIT EL HADJ</div>
<div class="text-slate-500 text-sm">@yae96 · Lead Dev</div>
</div>
</div>
</div>
<!-- Testimonial 3 -->
<div class="bg-white rounded-xl shadow-md p-6 border border-slate-100">
<div class="flex items-center mb-4">
<div class="text-amber-400 flex">
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
</div>
</div>
<blockquote class="text-slate-700 mb-6">
"Been using PHPFlasher in our Symfony app for 6+ months. Had an issue with modal stacking, reached out on GitHub and got a fix in like 2 days! That kind of support is rare for open source. Huge thanks to @younes!"
</blockquote>
<div class="flex items-center">
<img src="https://github.com/youssefsaoubou.png" alt="Youssef Saoubou" class="w-10 h-10 rounded-full mr-3 object-cover">
<div>
<div class="font-medium text-slate-800">Youssef Saoubou</div>
<div class="text-slate-500 text-sm">@youssefsaoubou · Backend Dev</div>
</div>
</div>
</div>
</div>
</section>
<!-- Community Section -->
<section class="bg-gradient-to-b from-slate-50 to-white py-16 mb-20">
<div class="container mx-auto px-4">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Community</div>
<h2 class="text-3xl sm:text-4xl font-bold text-slate-800 mb-3">Join Our Growing Community</h2>
<p class="text-slate-600 max-w-2xl mx-auto">Help us make PHPFlasher even better</p>
</div>
<div class="max-w-5xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- GitHub -->
<a href="https://github.com/php-flasher/php-flasher" class="group bg-white rounded-xl shadow-md hover:shadow-lg transition-all duration-300 border border-slate-100 p-8 flex flex-col items-center text-center transform hover:-translate-y-1">
<div class="w-16 h-16 bg-slate-50 rounded-full flex items-center justify-center mb-5 group-hover:bg-slate-100 transition-colors">
<i class="fa-brands fa-github text-slate-800 text-3xl"></i>
</div>
<h3 class="font-semibold text-xl text-slate-800 mb-3">Star on GitHub</h3>
<p class="text-slate-600 mb-6">Contribute to the project, report issues, or suggest new features.</p>
<div class="mt-auto px-6 py-2.5 bg-slate-100 rounded-full text-slate-700 text-sm font-medium group-hover:bg-slate-200 transition-colors">
<i class="fa-regular fa-star mr-1.5"></i> Star Project
</div>
</a>
<!-- Discussions -->
<a href="https://github.com/php-flasher/php-flasher/discussions" class="group bg-white rounded-xl shadow-md hover:shadow-lg transition-all duration-300 border border-slate-100 p-8 flex flex-col items-center text-center transform hover:-translate-y-1">
<div class="w-16 h-16 bg-indigo-50 rounded-full flex items-center justify-center mb-5 group-hover:bg-indigo-100 transition-colors">
<i class="fa-solid fa-comments text-indigo-600 text-3xl"></i>
</div>
<h3 class="font-semibold text-xl text-slate-800 mb-3">Join Discussions</h3>
<p class="text-slate-600 mb-6">Ask questions, share ideas, and connect with other developers.</p>
<div class="mt-auto px-6 py-2.5 bg-indigo-100 rounded-full text-indigo-700 text-sm font-medium group-hover:bg-indigo-200 transition-colors">
<i class="fa-regular fa-message mr-1.5"></i> Participate
</div>
</a>
<!-- Documentation -->
<a href="/laravel" class="group bg-white rounded-xl shadow-md hover:shadow-lg transition-all duration-300 border border-slate-100 p-8 flex flex-col items-center text-center transform hover:-translate-y-1">
<div class="w-16 h-16 bg-amber-50 rounded-full flex items-center justify-center mb-5 group-hover:bg-amber-100 transition-colors">
<i class="fa-solid fa-book-open text-amber-600 text-3xl"></i>
</div>
<h3 class="font-semibold text-xl text-slate-800 mb-3">Read Documentation</h3>
<p class="text-slate-600 mb-6">Explore comprehensive guides, examples, and API reference.</p>
<div class="mt-auto px-6 py-2.5 bg-amber-100 rounded-full text-amber-700 text-sm font-medium group-hover:bg-amber-200 transition-colors">
<i class="fa-solid fa-arrow-right mr-1.5"></i> Get Started
</div>
</a>
</div>
</div>
</section>
<!-- Call To Action -->
<section class="container mx-auto px-4 mb-16">
<div class="bg-gradient-to-r from-indigo-500 to-purple-600 rounded-2xl overflow-hidden shadow-lg">
<div class="max-w-4xl mx-auto px-6 py-16 text-center">
<h2 class="text-3xl md:text-4xl font-bold text-white mb-4">Ready to Enhance Your PHP Applications?</h2>
<p class="text-indigo-100 text-lg mb-8 max-w-2xl mx-auto">
Add beautiful notifications to your Laravel or Symfony applications in minutes with PHPFlasher.
</p>
<div class="flex flex-wrap justify-center gap-4">
<a href="#quick-start" class="px-6 py-3 bg-white hover:bg-slate-100 text-indigo-600 font-medium rounded-lg transition-colors duration-200 shadow-md flex items-center">
<i class="fa-solid fa-rocket mr-2"></i> Get Started
</a>
<a href="https://github.com/php-flasher/php-flasher" class="px-6 py-3 bg-indigo-700 hover:bg-indigo-800 text-white font-medium rounded-lg transition-colors duration-200 shadow-md flex items-center">
<i class="fa-brands fa-github mr-2"></i> Star on GitHub
</a>
</div>
</div>
</div>
</section>
<!-- Browser Support Section -->
<section class="container mx-auto px-4 mb-16">
<div class="text-center mb-8">
<div class="inline-block px-3 py-1 bg-slate-100 text-slate-700 rounded-full text-sm font-medium mb-2">Support</div>
<h2 class="text-xl font-bold text-slate-800">Compatible Everywhere</h2>
</div>
<div class="bg-white rounded-xl shadow-sm border border-slate-100 py-8">
<div class="flex flex-wrap justify-center gap-10 px-4">
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-brands fa-chrome text-green-500 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Chrome</span>
</div>
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-brands fa-firefox text-orange-500 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Firefox</span>
</div>
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-brands fa-safari text-blue-500 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Safari</span>
</div>
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-brands fa-edge text-blue-600 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Edge</span>
</div>
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-solid fa-mobile-screen text-slate-600 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Mobile</span>
</div>
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-solid fa-moon text-indigo-500 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Dark Mode</span>
</div>
</div>
</div>
</section>
</div>
{% include footer.html %}
{% include scripts.html %}
</body>
</html>
+1 -1
View File
@@ -2,7 +2,7 @@
"entrypoints": {
"main": {
"css": [
"/dist/main.7cce6945.css"
"/dist/main.2e8d1953.css"
],
"js": [
"/dist/main.8b056786.js"
File diff suppressed because one or more lines are too long
+741 -1
View File
@@ -1,5 +1,745 @@
---
permalink: /
description: PHPFlasher is a powerful PHP notification library that makes it simple to add beautiful flash messages to your Laravel or Symfony applications. Perfect for form submissions, API responses, and user interactions.
layout: home
---
<div class="container max-w-7xl mx-auto px-4 lg:px-6 py-4">
<!-- Hero Section with Animated Bell in SVG -->
<section class="relative overflow-hidden bg-gradient-to-b from-indigo-50 to-white pt-16 pb-12 rounded-3xl mb-16 shadow-sm">
<div class="absolute inset-0 overflow-hidden">
<div class="absolute left-0 top-0 w-1/3 h-1/3 bg-gradient-to-br from-purple-100 to-transparent opacity-60 blur-3xl"></div>
<div class="absolute right-0 bottom-0 w-1/3 h-1/3 bg-gradient-to-tl from-indigo-100 to-transparent opacity-60 blur-3xl"></div>
</div>
<div class="relative z-10 container mx-auto px-4">
<div class="text-center">
<div class="flex justify-center mb-8 animate-fade-in">
<div class="relative inline-block">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="320" height="50" viewBox="0 0 5082 781" preserveAspectRatio="xMidYMid meet" class="mx-auto">
<g transform="translate(0.000000,781.000000) scale(0.100000,-0.100000)" fill="#FFFFFF" stroke="none">
<!-- Bell path with animation class -->
<path id="bell-icon" class="animate-bell" fill="#4052B5" d="M45570 7540 c-283 -43 -490 -282 -490 -565 0 -200 118 -404 284 -491 31 -16 55 -32 53 -35 -1 -4 -39 -34 -85 -68 -439 -325 -739 -810 -843 -1365 -27 -142 -37 -465 -19 -617 31 -272 65 -381 300 -954 259 -633 265 -653 265 -955 0 -149 -4 -215 -18 -280 -32 -153 -79 -282 -161 -440 -44 -85 -95 -184 -113 -220 -101 -198 -102 -473 -3 -671 115 -230 303 -375 550 -425 98 -19 249 -15 343 10 31 9 363 142 738 296 374 154 683 280 687 280 4 0 16 -30 26 -66 43 -151 158 -332 273 -432 263 -226 594 -301 920 -206 153 44 272 114 392 228 93 90 150 169 205 287 68 144 90 246 89 409 -1 162 -28 283 -96 422 l-42 88 45 19 c25 10 326 135 670 276 344 141 657 273 695 293 102 53 247 200 302 307 60 117 84 203 90 325 13 240 -73 452 -250 616 -94 88 -205 147 -372 198 -66 21 -151 49 -190 63 -322 119 -616 373 -784 677 -27 50 -136 300 -241 554 -206 500 -223 539 -308 676 -338 546 -876 904 -1521 1013 -145 25 -513 24 -653 0 -137 -24 -128 -29 -105 56 32 125 13 277 -51 402 -102 201 -356 329 -582 295z"/>
<path fill="#2D264B" d="M19237 6013 c-4 -3 -7 -1252 -7 -2775 l0 -2768 640 0 640 0 0 2775 0 2775 -633 0 c-349 0 -637 -3 -640 -7z"/>
<path fill="#2D264B" d="M30710 3245 l0 -2775 640 0 640 0 0 1188 c0 1271 1 1296 51 1444 43 129 97 218 188 308 131 131 259 188 464 210 75 8 131 8 199 1 345 -39 574 -235 670 -576 l23 -80 3 -1247 3 -1248 640 0 640 0 -4 1348 c-3 1322 -3 1349 -25 1479 -84 520 -288 873 -657 1138 -428 307 -1127 349 -1655 99 -185 -88 -377 -237 -503 -394 l-37 -45 0 963 0 962 -640 0 -640 0 0 -2775z"/>
<path fill="#4052B5" d="M300 3100 l0 -2630 635 0 635 0 0 944 0 944 538 5 c576 5 639 9 881 63 482 108 880 386 1100 769 100 174 176 412 201 627 18 149 8 429 -20 578 -79 425 -298 769 -636 996 -234 156 -489 250 -832 306 -135 22 -154 22 -1319 25 l-1183 4 0 -2631z m2239 1571 c223 -58 362 -183 427 -385 22 -66 27 -106 31 -216 6 -166 -10 -251 -68 -368 -71 -145 -189 -239 -369 -293 -72 -22 -92 -23 -532 -27 l-458 -3 0 654 c0 359 3 657 7 661 4 4 204 6 443 3 408 -3 440 -5 519 -26z"/>
<path fill="#4052B5" d="M4970 3100 l0 -2630 640 0 640 0 0 1085 0 1085 1000 0 1000 0 0 -1085 0 -1085 640 0 640 0 0 2630 0 2630 -640 0 -640 0 0 -1030 0 -1030 -1000 0 -1000 0 0 1030 0 1030 -640 0 -640 0 0 -2630z"/>
<path fill="#4052B5" d="M10460 3100 l0 -2630 635 0 635 0 0 944 0 944 538 5 c576 5 639 9 881 63 343 77 630 232 866 469 132 131 199 224 275 380 127 258 177 510 167 834 -8 275 -54 479 -158 700 -141 299 -362 524 -680 690 -181 95 -394 160 -657 203 -135 22 -154 22 -1319 25 l-1183 4 0 -2631z m2239 1571 c115 -30 199 -72 267 -134 126 -114 183 -252 191 -457 15 -357 -130 -578 -437 -671 -72 -22 -92 -23 -532 -27 l-458 -3 0 654 c0 359 3 657 7 661 4 4 204 6 443 3 408 -3 440 -5 519 -26z"/>
<path fill="#2D264B" d="M15130 3100 l0 -2630 640 0 640 0 0 1065 0 1065 805 0 805 0 0 495 0 495 -802 2 -803 3 0 555 0 555 1073 3 1072 2 0 510 0 510 -1715 0 -1715 0 0 -2630z"/>
<path fill="#2D264B" d="M22776 4695 c-704 -100 -1262 -603 -1481 -1335 -112 -373 -137 -881 -65 -1300 101 -589 393 -1067 829 -1361 433 -292 1009 -369 1511 -203 273 91 532 273 692 487 l58 79 0 -296 0 -296 640 0 640 0 0 2090 0 2090 -640 0 -640 0 -2 -291 -3 -291 -43 61 c-128 182 -349 356 -578 456 -95 41 -223 79 -341 101 -122 22 -447 28 -577 9z m854 -1126 c156 -39 317 -138 441 -274 176 -192 259 -459 246 -790 -12 -304 -95 -519 -270 -702 -135 -140 -281 -223 -456 -258 -128 -26 -337 -17 -451 20 -236 76 -406 216 -520 428 -80 147 -118 301 -127 512 -19 439 134 771 446 963 93 57 239 109 344 122 97 12 254 2 347 -21z"/>
<path fill="#2D264B" d="M27855 4699 c-696 -66 -1217 -427 -1369 -950 -36 -124 -50 -261 -42 -408 14 -287 94 -485 266 -661 242 -247 448 -339 1170 -520 456 -115 702 -208 774 -292 122 -142 63 -361 -120 -452 -85 -42 -175 -59 -319 -59 -100 1 -148 6 -208 22 -221 59 -386 215 -428 404 l-12 57 -629 0 -628 0 0 -36 c0 -57 37 -225 71 -321 72 -203 168 -353 334 -518 292 -293 685 -471 1185 -537 124 -16 538 -16 655 0 313 44 578 131 790 262 280 172 467 418 546 717 26 99 36 322 20 446 -41 309 -186 542 -467 753 -171 127 -521 256 -1079 395 -486 122 -635 188 -702 311 -26 48 -25 198 2 253 66 134 232 201 475 192 192 -7 316 -55 430 -167 71 -69 120 -161 144 -272 l6 -28 591 0 591 0 -7 53 c-22 167 -85 377 -155 517 -100 198 -285 414 -467 543 -225 160 -528 263 -867 296 -126 13 -419 12 -551 0z"/>
<path fill="#2D264B" d="M37390 4704 c-340 -40 -571 -103 -813 -225 -298 -150 -537 -361 -724 -639 -204 -305 -321 -686 -342 -1122 -28 -570 90 -1048 357 -1443 192 -285 415 -480 727 -635 228 -113 428 -173 705 -211 144 -20 518 -17 660 4 478 74 859 262 1161 574 164 169 302 371 382 559 38 89 97 259 97 281 0 11 -121 13 -679 13 l-680 0 -38 -72 c-77 -146 -231 -265 -399 -308 -322 -83 -667 18 -843 247 -92 119 -157 290 -177 462 l-7 61 1451 0 1450 0 9 78 c13 120 10 515 -5 627 -69 522 -310 986 -667 1284 -286 238 -615 382 -1020 447 -95 15 -521 28 -605 18z m423 -1040 c221 -46 420 -194 508 -378 39 -81 69 -202 69 -277 l0 -39 -800 0 c-770 0 -800 1 -800 18 0 47 45 186 89 277 115 236 326 380 610 415 82 11 234 3 324 -16z"/>
<path fill="#2D264B" d="M42860 4693 c-173 -12 -381 -68 -536 -144 -215 -105 -440 -292 -590 -492 l-74 -98 0 346 0 345 -640 0 -640 0 0 -2090 0 -2090 640 0 640 0 0 1029 c0 884 2 1047 16 1153 9 67 20 128 25 134 5 6 10 20 12 32 2 12 6 25 10 28 4 4 7 15 7 24 0 9 18 52 40 96 101 198 323 325 640 364 46 5 191 10 322 10 l238 0 0 680 0 680 -32 -2 c-18 -1 -53 -3 -78 -5z"/>
</g>
</svg>
</div>
</div>
<h1 class="text-3xl md:text-4xl font-bold text-slate-800 mb-4 tracking-tight">
Beautiful Flash Notifications <span class="text-indigo-600">Made Simple</span>
</h1>
<p class="text-lg text-slate-600 max-w-2xl mx-auto mb-8">
Add elegant notifications to your Laravel or Symfony applications with just <span class="bg-indigo-50 text-indigo-700 px-2 py-1 rounded">one line of code</span>. Perfect for developers of all skill levels.
</p>
<!--
<div class="flex flex-wrap gap-3 justify-center mb-8">
<a href="#quick-start" class="px-5 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors duration-200 shadow-md hover:shadow-lg flex items-center">
<i class="fa-solid fa-rocket mr-2"></i> Get Started
</a>
<a href="https://github.com/php-flasher/php-flasher" class="px-5 py-3 bg-white hover:bg-slate-50 text-slate-700 font-medium rounded-lg border border-slate-200 transition-colors duration-200 flex items-center">
<i class="fa-brands fa-github mr-2"></i> View on GitHub
</a>
</div>
-->
<div class="flex flex-wrap justify-center gap-3">
<a href="https://www.linkedin.com/in/younes--ennaji/" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/badge/author-@yoeunes-blue.svg" alt="Author Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://github.com/php-flasher/php-flasher" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/badge/source-php--flasher/php--flasher-blue.svg" alt="Source Code Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://github.com/php-flasher/php-flasher/releases" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/github/tag/php-flasher/flasher.svg" alt="GitHub Release Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://github.com/php-flasher/flasher/blob/master/LICENSE" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/badge/license-MIT-brightgreen.svg" alt="License Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://packagist.org/packages/php-flasher/flasher" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/packagist/dt/php-flasher/flasher.svg" alt="Packagist Downloads Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://github.com/php-flasher/php-flasher" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/github/stars/php-flasher/php-flasher.svg" alt="GitHub Stars Badge" class="transform hover:scale-105 transition-transform" />
</a>
<a href="https://packagist.org/packages/php-flasher/flasher" class="text-slate-500 hover:text-indigo-700 transition-colors duration-200">
<img src="https://img.shields.io/packagist/php-v/php-flasher/flasher.svg" alt="Supported PHP Version Badge" class="transform hover:scale-105 transition-transform" />
</a>
</div>
</div>
</div>
</section>
{% include flasher-studio.html %}
<!-- Quick Start Section (Moved before Features) -->
<section id="quick-start" class="container mx-auto px-4 mb-16">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Get Started</div>
<h2 class="text-2xl md:text-3xl font-bold text-slate-800 mb-2">Quick Installation</h2>
<p class="text-slate-600 max-w-2xl mx-auto">Get up and running in less than a minute</p>
</div>
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-xl shadow-md overflow-hidden border border-slate-100 mb-8">
<div class="bg-slate-800 p-4 flex items-center justify-between">
<div class="flex items-center">
<div class="flex space-x-1 mr-4">
<div class="w-3 h-3 rounded-full bg-red-400"></div>
<div class="w-3 h-3 rounded-full bg-yellow-400"></div>
<div class="w-3 h-3 rounded-full bg-green-400"></div>
</div>
<span class="text-slate-400 text-sm">Terminal</span>
</div>
<div class="text-slate-400 text-sm">Installation</div>
</div>
<div class="p-6 bg-slate-900">
<div class="flex flex-col gap-4">
<div class="relative">
<div class="flex items-center mb-2">
<div class="w-5 h-5 bg-red-500 text-white rounded-full flex items-center justify-center mr-2 text-xs">
<i class="fa-brands fa-laravel"></i>
</div>
<span class="text-slate-300 text-sm">Laravel Installation</span>
</div>
<pre class="bg-slate-800 p-4 rounded-lg text-sm overflow-x-auto"><code class="language-bash text-slate-200">composer require php-flasher/flasher-laravel</code></pre>
</div>
<div class="relative">
<div class="flex items-center mb-2">
<div class="w-5 h-5 bg-black text-white rounded-full flex items-center justify-center mr-2 text-xs">
<i class="fa-brands fa-symfony"></i>
</div>
<span class="text-slate-300 text-sm">Symfony Installation</span>
</div>
<pre class="bg-slate-800 p-4 rounded-lg text-sm overflow-x-auto"><code class="language-bash text-slate-200">composer require php-flasher/flasher-symfony</code></pre>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Laravel Quick Start -->
<a href="/laravel/" class="group bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 overflow-hidden hover:border-red-200">
<div class="p-6">
<div class="flex items-center mb-4">
<div class="w-10 h-10 bg-red-50 text-red-500 rounded-lg flex items-center justify-center mr-3 group-hover:bg-red-100 transition-colors">
<i class="fa-brands fa-laravel text-lg"></i>
</div>
<h3 class="text-lg font-semibold text-slate-800">Laravel Guide</h3>
</div>
<p class="text-slate-600 mb-4">Perfect integration with Laravel's notification system. Get started in minutes.</p>
<div class="flex items-center text-red-600 font-medium">
Learn more
<i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
<!-- Symfony Quick Start -->
<a href="/symfony/" class="group bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 overflow-hidden hover:border-slate-300">
<div class="p-6">
<div class="flex items-center mb-4">
<div class="w-10 h-10 bg-slate-50 text-black rounded-lg flex items-center justify-center mr-3 group-hover:bg-slate-100 transition-colors">
<i class="fa-brands fa-symfony text-lg"></i>
</div>
<h3 class="text-lg font-semibold text-slate-800">Symfony Guide</h3>
</div>
<p class="text-slate-600 mb-4">Complete Symfony integration with bundle configuration and services.</p>
<div class="flex items-center text-slate-700 font-medium">
Learn more
<i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6">
<!-- Livewire Quick Start -->
<a href="/livewire/" class="group bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 overflow-hidden hover:border-purple-200">
<div class="p-6">
<div class="flex items-center mb-4">
<div class="w-10 h-10 bg-purple-50 text-purple-600 rounded-lg flex items-center justify-center mr-3 group-hover:bg-purple-100 transition-colors">
<i class="fa-solid fa-bolt text-lg"></i>
</div>
<h3 class="text-lg font-semibold text-slate-800">Livewire Guide</h3>
</div>
<p class="text-slate-600 mb-4">Real-time notifications in your Laravel Livewire components.</p>
<div class="flex items-center text-purple-600 font-medium">
Learn more
<i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
<!-- Inertia Quick Start -->
<a href="/inertia/" class="group bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 overflow-hidden hover:border-blue-200">
<div class="p-6">
<div class="flex items-center mb-4">
<div class="w-10 h-10 bg-blue-50 text-blue-600 rounded-lg flex items-center justify-center mr-3 group-hover:bg-blue-100 transition-colors">
<i class="fa-solid fa-arrows-rotate text-lg"></i>
</div>
<h3 class="text-lg font-semibold text-slate-800">Inertia Guide</h3>
</div>
<p class="text-slate-600 mb-4">Modern SPA notifications with Inertia.js integration.</p>
<div class="flex items-center text-blue-600 font-medium">
Learn more
<i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
</div>
</div>
</section>
<!-- Features Section -->
<section class="container mx-auto px-4 mb-20">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Features</div>
<h2 class="text-3xl sm:text-4xl font-bold text-slate-800 mb-3">Why Choose PHPFlasher?</h2>
<p class="text-slate-600 max-w-2xl mx-auto">Everything you need to add beautiful notifications to your PHP applications</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto">
<!-- Feature 1 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-indigo-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-wand-magic-sparkles text-indigo-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="easy-to-use">Easy to Use</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Simple one-line code to show notifications</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Works right out of the box - zero configuration</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Intuitive, consistent API across libraries</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Customize notifications with ease</span>
</li>
</ul>
</div>
<!-- Feature 2 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-purple-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-sliders text-purple-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="flexible">Flexible & Powerful</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Multiple notification libraries in one package</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Session and direct rendering options</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Customizable positions, animations, and styles</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Flash messages across redirects</span>
</li>
</ul>
</div>
<!-- Feature 3 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-blue-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-code text-blue-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="integration">Framework Integration</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-brands fa-laravel text-red-500 mt-1 mr-2.5"></i>
<span>Native Laravel integration with facades</span>
</li>
<li class="flex items-start">
<i class="fa-brands fa-symfony text-black mt-1 mr-2.5"></i>
<span>Seamless Symfony support with bundle</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-bolt text-purple-500 mt-1 mr-2.5"></i>
<span>Livewire real-time notifications</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-arrows-rotate text-blue-500 mt-1 mr-2.5"></i>
<span>Inertia.js SPA support</span>
</li>
</ul>
</div>
<!-- Feature 4 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-emerald-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-user-gear text-emerald-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="developer-friendly">Developer Experience</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Full IDE support with autocompletion</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Extensive documentation with examples</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>TypeScript support for JavaScript usage</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Well-tested, production-ready code</span>
</li>
</ul>
</div>
<!-- Feature 5 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-amber-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-rocket text-amber-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="performance">Performance Optimized</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Lightweight core with lazy-loading</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Only loads required JavaScript libraries</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Optimized asset loading strategies</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Minimal framework overhead</span>
</li>
</ul>
</div>
<!-- Feature 6 -->
<div class="bg-white rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 p-6 flex flex-col h-full transform hover:-translate-y-1">
<div class="w-14 h-14 bg-pink-50 rounded-xl flex items-center justify-center mb-5">
<i class="fa-solid fa-palette text-pink-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-slate-800 mb-3" id="customizable">Highly Customizable</h3>
<ul class="space-y-3 text-slate-600 flex-grow">
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Easily style notifications to match your brand</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Configure default settings globally</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Override options per notification</span>
</li>
<li class="flex items-start">
<i class="fa-solid fa-check text-green-500 mt-1 mr-2.5"></i>
<span>Create custom themes and templates</span>
</li>
</ul>
</div>
</div>
</section>
<!-- Library Showcase Section -->
<section class="container mx-auto px-4 mb-20">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Libraries</div>
<h2 class="text-3xl sm:text-4xl font-bold text-slate-800 mb-3">Choose Your Preferred Library</h2>
<p class="text-slate-600 max-w-2xl mx-auto">PHPFlasher integrates with popular JavaScript notification libraries</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 max-w-5xl mx-auto">
<!-- Toastr -->
<a href="/library/toastr/" class="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 border border-slate-100 overflow-hidden flex flex-col h-full transform hover:-translate-y-1">
<div class="h-36 bg-gradient-to-br from-blue-400 to-blue-600 flex items-center justify-center relative overflow-hidden">
<div class="absolute inset-0 bg-blue-600 opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="w-20 h-20 bg-white rounded-full flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300">
<i class="fa-solid fa-message text-blue-500 text-3xl"></i>
</div>
</div>
<div class="p-6 flex-grow">
<h3 class="font-semibold text-lg text-slate-800 mb-2">Toastr.js</h3>
<p class="text-slate-600 text-sm mb-4">Simple toast notifications for web applications. Clean, elegant and customizable.</p>
<div class="flex items-center text-blue-600 font-medium text-sm mt-auto group-hover:underline">
Learn more <i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
<!-- SweetAlert2 -->
<a href="/library/sweetalert/" class="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 border border-slate-100 overflow-hidden flex flex-col h-full transform hover:-translate-y-1">
<div class="h-36 bg-gradient-to-br from-pink-400 to-red-500 flex items-center justify-center relative overflow-hidden">
<div class="absolute inset-0 bg-red-500 opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="w-20 h-20 bg-white rounded-full flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300">
<i class="fa-solid fa-circle-exclamation text-pink-500 text-3xl"></i>
</div>
</div>
<div class="p-6 flex-grow">
<h3 class="font-semibold text-lg text-slate-800 mb-2">SweetAlert2</h3>
<p class="text-slate-600 text-sm mb-4">Beautiful, responsive, customizable alert dialogs with interactive elements.</p>
<div class="flex items-center text-pink-600 font-medium text-sm mt-auto group-hover:underline">
Learn more <i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
<!-- Noty -->
<a href="/library/noty/" class="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 border border-slate-100 overflow-hidden flex flex-col h-full transform hover:-translate-y-1">
<div class="h-36 bg-gradient-to-br from-purple-400 to-indigo-600 flex items-center justify-center relative overflow-hidden">
<div class="absolute inset-0 bg-indigo-600 opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="w-20 h-20 bg-white rounded-full flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300">
<i class="fa-solid fa-bell text-purple-500 text-3xl"></i>
</div>
</div>
<div class="p-6 flex-grow">
<h3 class="font-semibold text-lg text-slate-800 mb-2">Noty</h3>
<p class="text-slate-600 text-sm mb-4">Highly customizable notification library with multiple positions and animations.</p>
<div class="flex items-center text-purple-600 font-medium text-sm mt-auto group-hover:underline">
Learn more <i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
<!-- Notyf -->
<a href="/library/notyf/" class="group bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 border border-slate-100 overflow-hidden flex flex-col h-full transform hover:-translate-y-1">
<div class="h-36 bg-gradient-to-br from-green-400 to-teal-500 flex items-center justify-center relative overflow-hidden">
<div class="absolute inset-0 bg-teal-500 opacity-0 group-hover:opacity-10 transition-opacity"></div>
<div class="w-20 h-20 bg-white rounded-full flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300">
<i class="fa-solid fa-comment-dots text-green-500 text-3xl"></i>
</div>
</div>
<div class="p-6 flex-grow">
<h3 class="font-semibold text-lg text-slate-800 mb-2">Notyf</h3>
<p class="text-slate-600 text-sm mb-4">Minimalist toast notifications with a clean design and smooth animations.</p>
<div class="flex items-center text-green-600 font-medium text-sm mt-auto group-hover:underline">
Learn more <i class="fa-solid fa-arrow-right ml-1 transform group-hover:translate-x-1 transition-transform"></i>
</div>
</div>
</a>
</div>
<div class="text-center mt-10">
<a href="/library/toastr/" class="inline-flex items-center px-6 py-3 bg-white border border-slate-200 hover:bg-slate-50 text-slate-700 rounded-lg font-medium transition-colors duration-200 shadow-sm">
<i class="fa-solid fa-th-large mr-2"></i> Compare all libraries
</a>
</div>
</section>
<!-- Code Example Section -->
<section class="container mx-auto px-4 mb-16">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Code Examples</div>
<h2 class="text-2xl md:text-3xl font-bold text-slate-800 mb-2">Simple Implementation</h2>
<p class="text-slate-600 max-w-2xl mx-auto">Just a few lines of code to get started</p>
</div>
<div class="flex flex-col gap-8 max-w-4xl mx-auto">
<!-- PHP Example -->
<div class="bg-white rounded-xl shadow-md overflow-hidden border border-slate-100">
<div class="bg-slate-800 px-4 py-3 flex items-center">
<div class="flex space-x-1.5 mr-3">
<div class="w-2.5 h-2.5 rounded-full bg-red-500"></div>
<div class="w-2.5 h-2.5 rounded-full bg-yellow-500"></div>
<div class="w-2.5 h-2.5 rounded-full bg-green-500"></div>
</div>
<div class="text-white opacity-80 text-xs font-medium">Controller.php</div>
</div>
<div>
<pre class="bg-slate-50 rounded-lg p-4 text-sm overflow-x-auto"><code class="language-php">&lt;?php
namespace App\Http\Controllers;
class ProfileController
{
public function update()
{
// Update user profile logic
// Show success notification
flash()->success('Profile updated successfully!');
// Or add a title
flash()->success('Your changes have been saved.', 'Profile Updated');
// Or add a title and options
flash()
->option('timeout', 5000)
->option('position', 'top-right')
->success('Changes saved with custom settings');
// Change notification options
flash()->options([
'timeout' => 5000,
'position' => 'top-right'
])->success('Changes saved with custom settings');
return back();
}
}</code></pre>
</div>
</div>
<!-- JavaScript Example -->
<div class="bg-white rounded-xl shadow-md overflow-hidden border border-slate-100">
<div class="bg-slate-800 px-4 py-3 flex items-center">
<div class="flex space-x-1.5 mr-3">
<div class="w-2.5 h-2.5 rounded-full bg-red-500"></div>
<div class="w-2.5 h-2.5 rounded-full bg-yellow-500"></div>
<div class="w-2.5 h-2.5 rounded-full bg-green-500"></div>
</div>
<div class="text-white opacity-80 text-xs font-medium">app.js</div>
</div>
<div>
<pre class="bg-slate-50 rounded-lg p-4 text-sm overflow-x-auto"><code class="language-javascript">// Form submission example
document.getElementById('contact-form').addEventListener('submit', async function(e) {
e.preventDefault();
try {
const response = await submitForm(this);
if (response.success) {
// Show success notification
flasher.success('Your message has been sent!', 'Thank You');
} else {
// Show error notification
flasher.error('Please check your form inputs', 'Error');
}
} catch (error) {
// Show error notification with details
flasher.error('Server error, please try again later.', 'Error');
console.error(error);
}
});</code></pre>
</div>
</div>
</div>
</section>
<!-- Testimonials Section -->
<section class="container mx-auto px-4 mb-20">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Testimonials</div>
<h2 class="text-3xl sm:text-4xl font-bold text-slate-800 mb-3">Loved by Developers</h2>
<p class="text-slate-600 max-w-2xl mx-auto">See what others are saying about PHPFlasher</p>
</div>
<div class="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- Testimonial 1 -->
<div class="bg-white rounded-xl shadow-md p-6 border border-slate-100">
<div class="flex items-center mb-4">
<div class="text-amber-400 flex">
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
</div>
</div>
<blockquote class="text-slate-700 mb-6">
"Honestly wasn't expecting much, but wow! Saved me hours of headaches with our notification system. Just dropped it in and it worked first try. No more fighting with JS libraries!"
</blockquote>
<div class="flex items-center">
<img src="https://github.com/salmayno.png" alt="Salma Mourad" class="w-10 h-10 rounded-full mr-3 object-cover">
<div>
<div class="font-medium text-slate-800">Salma Mourad</div>
<div class="text-slate-500 text-sm">@salmayno · Freelancer</div>
</div>
</div>
</div>
<!-- Testimonial 2 -->
<div class="bg-white rounded-xl shadow-md p-6 border border-slate-100">
<div class="flex items-center mb-4">
<div class="text-amber-400 flex">
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star-half-stroke"></i>
</div>
</div>
<blockquote class="text-slate-700 mb-6">
"We were using Toastr directly but it was a mess maintaining it. Switched to PHPFlasher last sprint and it just works. The Livewire integration is a huge time saver too. Only nitpick is I wish the docs had more examples."
</blockquote>
<div class="flex items-center">
<img src="https://github.com/yae96.png" alt="Youness AIT EL HADJ" class="w-10 h-10 rounded-full mr-3 object-cover">
<div>
<div class="font-medium text-slate-800">Youness AIT EL HADJ</div>
<div class="text-slate-500 text-sm">@yae96 · Lead Dev</div>
</div>
</div>
</div>
<!-- Testimonial 3 -->
<div class="bg-white rounded-xl shadow-md p-6 border border-slate-100">
<div class="flex items-center mb-4">
<div class="text-amber-400 flex">
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
</div>
</div>
<blockquote class="text-slate-700 mb-6">
"Been using PHPFlasher in our Symfony app for 6+ months. Had an issue with modal stacking, reached out on GitHub and got a fix in like 2 days! That kind of support is rare for open source. Huge thanks to @younes!"
</blockquote>
<div class="flex items-center">
<img src="https://github.com/youssefsaoubou.png" alt="Youssef Saoubou" class="w-10 h-10 rounded-full mr-3 object-cover">
<div>
<div class="font-medium text-slate-800">Youssef Saoubou</div>
<div class="text-slate-500 text-sm">@youssefsaoubou · Backend Dev</div>
</div>
</div>
</div>
</div>
</section>
<!-- Community Section -->
<section class="bg-gradient-to-b from-slate-50 to-white py-16 mb-20">
<div class="container mx-auto px-4">
<div class="text-center mb-12">
<div class="inline-block px-3 py-1 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium mb-2">Community</div>
<h2 class="text-3xl sm:text-4xl font-bold text-slate-800 mb-3">Join Our Growing Community</h2>
<p class="text-slate-600 max-w-2xl mx-auto">Help us make PHPFlasher even better</p>
</div>
<div class="max-w-5xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- GitHub -->
<a href="https://github.com/php-flasher/php-flasher" class="group bg-white rounded-xl shadow-md hover:shadow-lg transition-all duration-300 border border-slate-100 p-8 flex flex-col items-center text-center transform hover:-translate-y-1">
<div class="w-16 h-16 bg-slate-50 rounded-full flex items-center justify-center mb-5 group-hover:bg-slate-100 transition-colors">
<i class="fa-brands fa-github text-slate-800 text-3xl"></i>
</div>
<h3 class="font-semibold text-xl text-slate-800 mb-3">Star on GitHub</h3>
<p class="text-slate-600 mb-6">Contribute to the project, report issues, or suggest new features.</p>
<div class="mt-auto px-6 py-2.5 bg-slate-100 rounded-full text-slate-700 text-sm font-medium group-hover:bg-slate-200 transition-colors">
<i class="fa-regular fa-star mr-1.5"></i> Star Project
</div>
</a>
<!-- Discussions -->
<a href="https://github.com/php-flasher/php-flasher/discussions" class="group bg-white rounded-xl shadow-md hover:shadow-lg transition-all duration-300 border border-slate-100 p-8 flex flex-col items-center text-center transform hover:-translate-y-1">
<div class="w-16 h-16 bg-indigo-50 rounded-full flex items-center justify-center mb-5 group-hover:bg-indigo-100 transition-colors">
<i class="fa-solid fa-comments text-indigo-600 text-3xl"></i>
</div>
<h3 class="font-semibold text-xl text-slate-800 mb-3">Join Discussions</h3>
<p class="text-slate-600 mb-6">Ask questions, share ideas, and connect with other developers.</p>
<div class="mt-auto px-6 py-2.5 bg-indigo-100 rounded-full text-indigo-700 text-sm font-medium group-hover:bg-indigo-200 transition-colors">
<i class="fa-regular fa-message mr-1.5"></i> Participate
</div>
</a>
<!-- Documentation -->
<a href="/laravel" class="group bg-white rounded-xl shadow-md hover:shadow-lg transition-all duration-300 border border-slate-100 p-8 flex flex-col items-center text-center transform hover:-translate-y-1">
<div class="w-16 h-16 bg-amber-50 rounded-full flex items-center justify-center mb-5 group-hover:bg-amber-100 transition-colors">
<i class="fa-solid fa-book-open text-amber-600 text-3xl"></i>
</div>
<h3 class="font-semibold text-xl text-slate-800 mb-3">Read Documentation</h3>
<p class="text-slate-600 mb-6">Explore comprehensive guides, examples, and API reference.</p>
<div class="mt-auto px-6 py-2.5 bg-amber-100 rounded-full text-amber-700 text-sm font-medium group-hover:bg-amber-200 transition-colors">
<i class="fa-solid fa-arrow-right mr-1.5"></i> Get Started
</div>
</a>
</div>
</div>
</section>
<!-- Call To Action -->
<section class="container mx-auto px-4 mb-16">
<div class="bg-gradient-to-r from-indigo-500 to-purple-600 rounded-2xl overflow-hidden shadow-lg">
<div class="max-w-4xl mx-auto px-6 py-16 text-center">
<h2 class="text-3xl md:text-4xl font-bold text-white mb-4">Ready to Enhance Your PHP Applications?</h2>
<p class="text-indigo-100 text-lg mb-8 max-w-2xl mx-auto">
Add beautiful notifications to your Laravel or Symfony applications in minutes with PHPFlasher.
</p>
<div class="flex flex-wrap justify-center gap-4">
<a href="#quick-start" class="px-6 py-3 bg-white hover:bg-slate-100 text-indigo-600 font-medium rounded-lg transition-colors duration-200 shadow-md flex items-center">
<i class="fa-solid fa-rocket mr-2"></i> Get Started
</a>
<a href="https://github.com/php-flasher/php-flasher" class="px-6 py-3 bg-indigo-700 hover:bg-indigo-800 text-white font-medium rounded-lg transition-colors duration-200 shadow-md flex items-center">
<i class="fa-brands fa-github mr-2"></i> Star on GitHub
</a>
</div>
</div>
</div>
</section>
<!-- Browser Support Section -->
<section class="container mx-auto px-4 mb-16">
<div class="text-center mb-8">
<div class="inline-block px-3 py-1 bg-slate-100 text-slate-700 rounded-full text-sm font-medium mb-2">Support</div>
<h2 class="text-xl font-bold text-slate-800">Compatible Everywhere</h2>
</div>
<div class="bg-white rounded-xl shadow-sm border border-slate-100 py-8">
<div class="flex flex-wrap justify-center gap-10 px-4">
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-brands fa-chrome text-green-500 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Chrome</span>
</div>
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-brands fa-firefox text-orange-500 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Firefox</span>
</div>
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-brands fa-safari text-blue-500 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Safari</span>
</div>
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-brands fa-edge text-blue-600 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Edge</span>
</div>
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-solid fa-mobile-screen text-slate-600 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Mobile</span>
</div>
<div class="flex flex-col items-center">
<div class="w-12 h-12 rounded-full bg-slate-50 flex items-center justify-center mb-2">
<i class="fa-solid fa-moon text-indigo-500 text-2xl"></i>
</div>
<span class="text-sm text-slate-600">Dark Mode</span>
</div>
</div>
</div>
</section>
</div>
+92 -92
View File
@@ -26,7 +26,7 @@
"@rollup/plugin-strip": "^3.0.4",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.2",
"@types/node": "^22.13.13",
"@types/node": "^22.13.14",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"all-contributors-cli": "^6.26.1",
@@ -46,7 +46,7 @@
"postcss-discard-comments": "^7.0.3",
"punycode": "^2.3.1",
"rimraf": "^6.0.1",
"rollup": "^4.37.0",
"rollup": "^4.38.0",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-clear": "^2.0.7",
"rollup-plugin-copy": "^3.5.0",
@@ -3269,9 +3269,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.37.0.tgz",
"integrity": "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.38.0.tgz",
"integrity": "sha512-ldomqc4/jDZu/xpYU+aRxo3V4mGCV9HeTgUBANI3oIQMOL+SsxB+S2lxMpkFp5UamSS3XuTMQVbsS24R4J4Qjg==",
"cpu": [
"arm"
],
@@ -3283,9 +3283,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.37.0.tgz",
"integrity": "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.38.0.tgz",
"integrity": "sha512-VUsgcy4GhhT7rokwzYQP+aV9XnSLkkhlEJ0St8pbasuWO/vwphhZQxYEKUP3ayeCYLhk6gEtacRpYP/cj3GjyQ==",
"cpu": [
"arm64"
],
@@ -3297,9 +3297,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.37.0.tgz",
"integrity": "sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.38.0.tgz",
"integrity": "sha512-buA17AYXlW9Rn091sWMq1xGUvWQFOH4N1rqUxGJtEQzhChxWjldGCCup7r/wUnaI6Au8sKXpoh0xg58a7cgcpg==",
"cpu": [
"arm64"
],
@@ -3311,9 +3311,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.37.0.tgz",
"integrity": "sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.38.0.tgz",
"integrity": "sha512-Mgcmc78AjunP1SKXl624vVBOF2bzwNWFPMP4fpOu05vS0amnLcX8gHIge7q/lDAHy3T2HeR0TqrriZDQS2Woeg==",
"cpu": [
"x64"
],
@@ -3325,9 +3325,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.37.0.tgz",
"integrity": "sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.38.0.tgz",
"integrity": "sha512-zzJACgjLbQTsscxWqvrEQAEh28hqhebpRz5q/uUd1T7VTwUNZ4VIXQt5hE7ncs0GrF+s7d3S4on4TiXUY8KoQA==",
"cpu": [
"arm64"
],
@@ -3339,9 +3339,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.37.0.tgz",
"integrity": "sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.38.0.tgz",
"integrity": "sha512-hCY/KAeYMCyDpEE4pTETam0XZS4/5GXzlLgpi5f0IaPExw9kuB+PDTOTLuPtM10TlRG0U9OSmXJ+Wq9J39LvAg==",
"cpu": [
"x64"
],
@@ -3353,9 +3353,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.37.0.tgz",
"integrity": "sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.38.0.tgz",
"integrity": "sha512-mimPH43mHl4JdOTD7bUMFhBdrg6f9HzMTOEnzRmXbOZqjijCw8LA5z8uL6LCjxSa67H2xiLFvvO67PT05PRKGg==",
"cpu": [
"arm"
],
@@ -3367,9 +3367,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.37.0.tgz",
"integrity": "sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.38.0.tgz",
"integrity": "sha512-tPiJtiOoNuIH8XGG8sWoMMkAMm98PUwlriOFCCbZGc9WCax+GLeVRhmaxjJtz6WxrPKACgrwoZ5ia/uapq3ZVg==",
"cpu": [
"arm"
],
@@ -3381,9 +3381,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.37.0.tgz",
"integrity": "sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.38.0.tgz",
"integrity": "sha512-wZco59rIVuB0tjQS0CSHTTUcEde+pXQWugZVxWaQFdQQ1VYub/sTrNdY76D1MKdN2NB48JDuGABP6o6fqos8mA==",
"cpu": [
"arm64"
],
@@ -3395,9 +3395,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.37.0.tgz",
"integrity": "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.38.0.tgz",
"integrity": "sha512-fQgqwKmW0REM4LomQ+87PP8w8xvU9LZfeLBKybeli+0yHT7VKILINzFEuggvnV9M3x1Ed4gUBmGUzCo/ikmFbQ==",
"cpu": [
"arm64"
],
@@ -3409,9 +3409,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.37.0.tgz",
"integrity": "sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.38.0.tgz",
"integrity": "sha512-hz5oqQLXTB3SbXpfkKHKXLdIp02/w3M+ajp8p4yWOWwQRtHWiEOCKtc9U+YXahrwdk+3qHdFMDWR5k+4dIlddg==",
"cpu": [
"loong64"
],
@@ -3423,9 +3423,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.37.0.tgz",
"integrity": "sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.38.0.tgz",
"integrity": "sha512-NXqygK/dTSibQ+0pzxsL3r4Xl8oPqVoWbZV9niqOnIHV/J92fe65pOir0xjkUZDRSPyFRvu+4YOpJF9BZHQImw==",
"cpu": [
"ppc64"
],
@@ -3437,9 +3437,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.37.0.tgz",
"integrity": "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.38.0.tgz",
"integrity": "sha512-GEAIabR1uFyvf/jW/5jfu8gjM06/4kZ1W+j1nWTSSB3w6moZEBm7iBtzwQ3a1Pxos2F7Gz+58aVEnZHU295QTg==",
"cpu": [
"riscv64"
],
@@ -3451,9 +3451,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.37.0.tgz",
"integrity": "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.38.0.tgz",
"integrity": "sha512-9EYTX+Gus2EGPbfs+fh7l95wVADtSQyYw4DfSBcYdUEAmP2lqSZY0Y17yX/3m5VKGGJ4UmIH5LHLkMJft3bYoA==",
"cpu": [
"riscv64"
],
@@ -3465,9 +3465,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.37.0.tgz",
"integrity": "sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.38.0.tgz",
"integrity": "sha512-Mpp6+Z5VhB9VDk7RwZXoG2qMdERm3Jw07RNlXHE0bOnEeX+l7Fy4bg+NxfyN15ruuY3/7Vrbpm75J9QHFqj5+Q==",
"cpu": [
"s390x"
],
@@ -3479,9 +3479,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.37.0.tgz",
"integrity": "sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.38.0.tgz",
"integrity": "sha512-vPvNgFlZRAgO7rwncMeE0+8c4Hmc+qixnp00/Uv3ht2x7KYrJ6ERVd3/R0nUtlE6/hu7/HiiNHJ/rP6knRFt1w==",
"cpu": [
"x64"
],
@@ -3493,9 +3493,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.37.0.tgz",
"integrity": "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.38.0.tgz",
"integrity": "sha512-q5Zv+goWvQUGCaL7fU8NuTw8aydIL/C9abAVGCzRReuj5h30TPx4LumBtAidrVOtXnlB+RZkBtExMsfqkMfb8g==",
"cpu": [
"x64"
],
@@ -3507,9 +3507,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.37.0.tgz",
"integrity": "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.38.0.tgz",
"integrity": "sha512-u/Jbm1BU89Vftqyqbmxdq14nBaQjQX1HhmsdBWqSdGClNaKwhjsg5TpW+5Ibs1mb8Es9wJiMdl86BcmtUVXNZg==",
"cpu": [
"arm64"
],
@@ -3521,9 +3521,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.37.0.tgz",
"integrity": "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.38.0.tgz",
"integrity": "sha512-mqu4PzTrlpNHHbu5qleGvXJoGgHpChBlrBx/mEhTPpnAL1ZAYFlvHD7rLK839LLKQzqEQMFJfGrrOHItN4ZQqA==",
"cpu": [
"ia32"
],
@@ -3535,9 +3535,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.37.0.tgz",
"integrity": "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.38.0.tgz",
"integrity": "sha512-jjqy3uWlecfB98Psxb5cD6Fny9Fupv9LrDSPTQZUROqjvZmcCqNu4UMl7qqhlUUGpwiAkotj6GYu4SZdcr/nLw==",
"cpu": [
"x64"
],
@@ -3804,9 +3804,9 @@
}
},
"node_modules/@types/estree": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"dev": true,
"license": "MIT"
},
@@ -3873,9 +3873,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.13.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz",
"integrity": "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==",
"version": "22.13.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
"integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -13311,13 +13311,13 @@
}
},
"node_modules/rollup": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.37.0.tgz",
"integrity": "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg==",
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.38.0.tgz",
"integrity": "sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.6"
"@types/estree": "1.0.7"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -13327,26 +13327,26 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.37.0",
"@rollup/rollup-android-arm64": "4.37.0",
"@rollup/rollup-darwin-arm64": "4.37.0",
"@rollup/rollup-darwin-x64": "4.37.0",
"@rollup/rollup-freebsd-arm64": "4.37.0",
"@rollup/rollup-freebsd-x64": "4.37.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.37.0",
"@rollup/rollup-linux-arm-musleabihf": "4.37.0",
"@rollup/rollup-linux-arm64-gnu": "4.37.0",
"@rollup/rollup-linux-arm64-musl": "4.37.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.37.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.37.0",
"@rollup/rollup-linux-riscv64-gnu": "4.37.0",
"@rollup/rollup-linux-riscv64-musl": "4.37.0",
"@rollup/rollup-linux-s390x-gnu": "4.37.0",
"@rollup/rollup-linux-x64-gnu": "4.37.0",
"@rollup/rollup-linux-x64-musl": "4.37.0",
"@rollup/rollup-win32-arm64-msvc": "4.37.0",
"@rollup/rollup-win32-ia32-msvc": "4.37.0",
"@rollup/rollup-win32-x64-msvc": "4.37.0",
"@rollup/rollup-android-arm-eabi": "4.38.0",
"@rollup/rollup-android-arm64": "4.38.0",
"@rollup/rollup-darwin-arm64": "4.38.0",
"@rollup/rollup-darwin-x64": "4.38.0",
"@rollup/rollup-freebsd-arm64": "4.38.0",
"@rollup/rollup-freebsd-x64": "4.38.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.38.0",
"@rollup/rollup-linux-arm-musleabihf": "4.38.0",
"@rollup/rollup-linux-arm64-gnu": "4.38.0",
"@rollup/rollup-linux-arm64-musl": "4.38.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.38.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.38.0",
"@rollup/rollup-linux-riscv64-gnu": "4.38.0",
"@rollup/rollup-linux-riscv64-musl": "4.38.0",
"@rollup/rollup-linux-s390x-gnu": "4.38.0",
"@rollup/rollup-linux-x64-gnu": "4.38.0",
"@rollup/rollup-linux-x64-musl": "4.38.0",
"@rollup/rollup-win32-arm64-msvc": "4.38.0",
"@rollup/rollup-win32-ia32-msvc": "4.38.0",
"@rollup/rollup-win32-x64-msvc": "4.38.0",
"fsevents": "~2.3.2"
}
},
+2 -2
View File
@@ -54,7 +54,7 @@
"@rollup/plugin-strip": "^3.0.4",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.2",
"@types/node": "^22.13.13",
"@types/node": "^22.13.14",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"all-contributors-cli": "^6.26.1",
@@ -74,7 +74,7 @@
"postcss-discard-comments": "^7.0.3",
"punycode": "^2.3.1",
"rimraf": "^6.0.1",
"rollup": "^4.37.0",
"rollup": "^4.38.0",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-clear": "^2.0.7",
"rollup-plugin-copy": "^3.5.0",
+10 -1
View File
@@ -193,7 +193,16 @@ final class FlasherServiceProvider extends PluginServiceProvider
$templateEngine = $app->make('flasher.template_engine');
$assetManager = $app->make('flasher.asset_manager');
$mainScript = $config->get('flasher.main_script');
$resources = $config->get('flasher.plugins');
$resources = [];
foreach ($config->get('flasher.plugins') as $name => $options) {
$resources[$name] = $options;
}
foreach ($config->get('flasher.themes') as $name => $options) {
$resources['theme.'.$name] = $options;
}
return new ResourceManager($templateEngine, $assetManager, $mainScript, $resources);
});
+9 -1
View File
@@ -26,6 +26,7 @@ namespace Illuminate\Contracts\Config;
* flash_bag: array<string, string[]>,
* presets: array<string, PresetType>,
* plugins: array<string, PluginType>,
* themes: array<string, ThemeType>,
* }
*
* @phpstan-type PresetType array{
@@ -40,6 +41,12 @@ namespace Illuminate\Contracts\Config;
* styles?: string[],
* options?: array<string, mixed>,
* }
*
* @phpstan-type ThemeType array{
* scripts?: string[],
* styles?: string[],
* options?: array<string, mixed>,
* }
*/
interface Repository
{
@@ -60,9 +67,10 @@ interface Repository
* ($key is 'flasher.filter' ? array<string, mixed> :
* ($key is 'flasher.presets' ? array<string, PresetType> :
* ($key is 'flasher.plugins' ? array<string, PluginType> :
* ($key is 'flasher.themes' ? array<string, ThemeType> :
* ($key is 'flasher.flash_bag' ? array<string, string[]> :
* ($key is 'flasher.excluded_paths' ? list<non-empty-string> :
* mixed))))))))
* mixed)))))))))
*/
public function get(string|array $key, mixed $default = null): mixed;
}
+214
View File
@@ -119,6 +119,7 @@ final class FlasherPlugin extends Plugin
$config = parent::normalizeConfig($config);
$config = $this->normalizePlugins($config);
$config = $this->normalizeThemes($config);
$config = $this->normalizePresets($config);
$config = $this->addDefaultConfig($config);
$config = $this->normalizeFlashBag($config);
@@ -189,6 +190,76 @@ final class FlasherPlugin extends Plugin
return $config;
}
/**
* Normalizes the themes configuration.
*
* This method ensures that the themes configuration has the correct structure
* and includes default themes unless explicitly disabled.
*
* @param array{
* scripts: string[],
* styles: string[],
* options: array<string, mixed>,
* plugins: array<string, mixed>,
* themes?: array<string, array{
* scripts?: string[],
* styles?: string[],
* options?: array<string, mixed>,
* }>,
* } $config The raw configuration
*
* @return array{
* scripts: string[],
* styles: string[],
* options: array<string, mixed>,
* plugins: array<string, mixed>,
* themes: array<string, mixed>,
* } The normalized configuration
*/
private function normalizeThemes(array $config): array
{
// Define default themes with their assets
$defaultThemes = $this->getDefaultThemes();
// Initialize themes config if not set
if (!isset($config['themes'])) {
$config['themes'] = [];
}
// Merge default themes with user-defined themes, prioritizing user configs
foreach ($defaultThemes as $themeName => $themeConfig) {
if (!isset($config['themes'][$themeName])) {
$config['themes'][$themeName] = $themeConfig;
} else {
// Make sure all required theme properties exist
if (!isset($config['themes'][$themeName]['scripts'])) {
$config['themes'][$themeName]['scripts'] = $themeConfig['scripts'];
}
if (!isset($config['themes'][$themeName]['styles'])) {
$config['themes'][$themeName]['styles'] = $themeConfig['styles'];
}
if (!isset($config['themes'][$themeName]['options'])) {
$config['themes'][$themeName]['options'] = $themeConfig['options'];
}
}
}
// Normalize theme configs
foreach ($config['themes'] as $name => $options) {
if (isset($options['scripts'])) {
$config['themes'][$name]['scripts'] = (array) $options['scripts'];
}
if (isset($options['styles'])) {
$config['themes'][$name]['styles'] = (array) $options['styles'];
}
if (!isset($options['options'])) {
$config['themes'][$name]['options'] = [];
}
}
return $config;
}
/**
* Normalizes the presets configuration.
*
@@ -379,4 +450,147 @@ final class FlasherPlugin extends Plugin
return $config;
}
/**
* Returns the default themes configuration.
*
* @return array<string, array{
* scripts: string[],
* styles: string[],
* options: array<string, mixed>
* }> The default themes configuration
*/
private function getDefaultThemes(): array
{
return [
'amazon' => [
'scripts' => ['/vendor/flasher/themes/amazon/amazon.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/amazon/amazon.min.css',
],
'options' => [],
],
'amber' => [
'scripts' => ['/vendor/flasher/themes/amber/amber.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/amber/amber.min.css',
],
'options' => [],
],
'jade' => [
'scripts' => ['/vendor/flasher/themes/jade/jade.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/jade/jade.min.css',
],
'options' => [],
],
'crystal' => [
'scripts' => ['/vendor/flasher/themes/crystal/crystal.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/crystal/crystal.min.css',
],
'options' => [],
],
'emerald' => [
'scripts' => ['/vendor/flasher/themes/emerald/emerald.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/emerald/emerald.min.css',
],
'options' => [],
],
'sapphire' => [
'scripts' => ['/vendor/flasher/themes/sapphire/sapphire.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/sapphire/sapphire.min.css',
],
'options' => [],
],
'ruby' => [
'scripts' => ['/vendor/flasher/themes/ruby/ruby.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/ruby/ruby.min.css',
],
'options' => [],
],
'onyx' => [
'scripts' => ['/vendor/flasher/themes/onyx/onyx.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/onyx/onyx.min.css',
],
'options' => [],
],
'neon' => [
'scripts' => ['/vendor/flasher/themes/neon/neon.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/neon/neon.min.css',
],
'options' => [],
],
'aurora' => [
'scripts' => ['/vendor/flasher/themes/aurora/aurora.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/aurora/aurora.min.css',
],
'options' => [],
],
'minimal' => [
'scripts' => ['/vendor/flasher/themes/minimal/minimal.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/minimal/minimal.min.css',
],
'options' => [],
],
'material' => [
'scripts' => ['/vendor/flasher/themes/material/material.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/material/material.min.css',
],
'options' => [],
],
'google' => [
'scripts' => ['/vendor/flasher/themes/google/google.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/google/google.min.css',
],
'options' => [],
],
'ios' => [
'scripts' => ['/vendor/flasher/themes/ios/ios.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/ios/ios.min.css',
],
'options' => [],
],
'slack' => [
'scripts' => ['/vendor/flasher/themes/slack/slack.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/slack/slack.min.css',
],
'options' => [],
],
'facebook' => [
'scripts' => ['/vendor/flasher/themes/facebook/facebook.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/facebook/facebook.min.css',
],
'options' => [],
],
];
}
}
@@ -26,6 +26,7 @@ final readonly class Configuration implements ConfigurationInterface
$this->addFlashBagSection($rootNode);
$this->addPresetsSection($rootNode);
$this->addPluginsSection($rootNode);
$this->addThemesSection($rootNode);
return $treeBuilder;
}
@@ -161,4 +162,34 @@ final readonly class Configuration implements ConfigurationInterface
->end()
->end();
}
private function addThemesSection(ArrayNodeDefinition $rootNode): void
{
$rootNode
->fixXmlConfig('theme')
->children()
->arrayNode('themes')
->info('Additional themes')
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->arrayNode('styles')
->info('CSS files for the theme')
->performNoDeepMerging()
->scalarPrototype()->end()
->end()
->arrayNode('scripts')
->info('JavaScript files for the theme')
->performNoDeepMerging()
->scalarPrototype()->end()
->end()
->arrayNode('options')
->info('Theme-specific options')
->variablePrototype()->end()
->end()
->end()
->end()
->end()
->end();
}
}
@@ -45,6 +45,7 @@ final class FlasherExtension extends AbstractExtension implements CompilerPassIn
* flash_bag: array<string, mixed>,
* filter: array<string, mixed>,
* plugins: array<array<string, mixed>>,
* themes: array<array<string, mixed>>,
* } $config
*/
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
@@ -72,6 +73,7 @@ final class FlasherExtension extends AbstractExtension implements CompilerPassIn
* flash_bag: array<string, mixed>,
* filter: array<string, mixed>,
* plugins: array<array<string, mixed>>,
* themes: array<array<string, mixed>>,
* } $config
*/
private function registerFlasherParameters(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
@@ -95,7 +97,8 @@ final class FlasherExtension extends AbstractExtension implements CompilerPassIn
->set('flasher.filter', $config['filter'])
->set('flasher.presets', $config['presets'])
->set('flasher.plugins', $config['plugins'])
;
->set('flasher.themes', $config['themes'])
->set('flasher.resources', $this->getFlasherResources($config));
}
private function registerServicesForAutoconfiguration(ContainerBuilder $builder): void
@@ -141,4 +144,29 @@ final class FlasherExtension extends AbstractExtension implements CompilerPassIn
$container->removeDefinition('flasher.flasher_listener');
}
/**
* Convert the Flasher configuration into a format that can be used by the ResourceManager.
*
* @param array{
* plugins: array<array<string, mixed>>,
* themes: array<array<string, mixed>>,
* } $config
*
* @return array<string, array<string, mixed>>
*/
private function getFlasherResources(array $config): array
{
$resources = [];
foreach ($config['plugins'] as $name => $options) {
$resources[$name] = $options;
}
foreach ($config['themes'] as $name => $options) {
$resources['theme.'.$name] = $options;
}
return $resources;
}
}
+1 -1
View File
@@ -127,7 +127,7 @@ return static function (ContainerConfigurator $container): void {
service('flasher.template_engine'),
service('flasher.asset_manager'),
param('flasher.main_script'),
param('flasher.plugins'),
param('flasher.resources'),
])
->set('flasher.response_manager', ResponseManager::class)
+130
View File
@@ -88,6 +88,136 @@ final class FlasherPluginTest extends TestCase
'options' => [],
],
],
'themes' => [
'amazon' => [
'scripts' => ['/vendor/flasher/themes/amazon/amazon.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/amazon/amazon.min.css',
],
'options' => [],
],
'amber' => [
'scripts' => ['/vendor/flasher/themes/amber/amber.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/amber/amber.min.css',
],
'options' => [],
],
'jade' => [
'scripts' => ['/vendor/flasher/themes/jade/jade.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/jade/jade.min.css',
],
'options' => [],
],
'crystal' => [
'scripts' => ['/vendor/flasher/themes/crystal/crystal.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/crystal/crystal.min.css',
],
'options' => [],
],
'emerald' => [
'scripts' => ['/vendor/flasher/themes/emerald/emerald.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/emerald/emerald.min.css',
],
'options' => [],
],
'sapphire' => [
'scripts' => ['/vendor/flasher/themes/sapphire/sapphire.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/sapphire/sapphire.min.css',
],
'options' => [],
],
'ruby' => [
'scripts' => ['/vendor/flasher/themes/ruby/ruby.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/ruby/ruby.min.css',
],
'options' => [],
],
'onyx' => [
'scripts' => ['/vendor/flasher/themes/onyx/onyx.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/onyx/onyx.min.css',
],
'options' => [],
],
'neon' => [
'scripts' => ['/vendor/flasher/themes/neon/neon.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/neon/neon.min.css',
],
'options' => [],
],
'aurora' => [
'scripts' => ['/vendor/flasher/themes/aurora/aurora.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/aurora/aurora.min.css',
],
'options' => [],
],
'minimal' => [
'scripts' => ['/vendor/flasher/themes/minimal/minimal.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/minimal/minimal.min.css',
],
'options' => [],
],
'material' => [
'scripts' => ['/vendor/flasher/themes/material/material.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/material/material.min.css',
],
'options' => [],
],
'google' => [
'scripts' => ['/vendor/flasher/themes/google/google.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/google/google.min.css',
],
'options' => [],
],
'ios' => [
'scripts' => ['/vendor/flasher/themes/ios/ios.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/ios/ios.min.css',
],
'options' => [],
],
'slack' => [
'scripts' => ['/vendor/flasher/themes/slack/slack.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/slack/slack.min.css',
],
'options' => [],
],
'facebook' => [
'scripts' => ['/vendor/flasher/themes/facebook/facebook.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/facebook/facebook.min.css',
],
'options' => [],
],
],
'filter' => [],
];
@@ -81,6 +81,136 @@ final class ConfigurationTest extends TestCase
'options' => [],
],
],
'themes' => [
'amazon' => [
'scripts' => ['/vendor/flasher/themes/amazon/amazon.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/amazon/amazon.min.css',
],
'options' => [],
],
'amber' => [
'scripts' => ['/vendor/flasher/themes/amber/amber.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/amber/amber.min.css',
],
'options' => [],
],
'jade' => [
'scripts' => ['/vendor/flasher/themes/jade/jade.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/jade/jade.min.css',
],
'options' => [],
],
'crystal' => [
'scripts' => ['/vendor/flasher/themes/crystal/crystal.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/crystal/crystal.min.css',
],
'options' => [],
],
'emerald' => [
'scripts' => ['/vendor/flasher/themes/emerald/emerald.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/emerald/emerald.min.css',
],
'options' => [],
],
'sapphire' => [
'scripts' => ['/vendor/flasher/themes/sapphire/sapphire.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/sapphire/sapphire.min.css',
],
'options' => [],
],
'ruby' => [
'scripts' => ['/vendor/flasher/themes/ruby/ruby.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/ruby/ruby.min.css',
],
'options' => [],
],
'onyx' => [
'scripts' => ['/vendor/flasher/themes/onyx/onyx.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/onyx/onyx.min.css',
],
'options' => [],
],
'neon' => [
'scripts' => ['/vendor/flasher/themes/neon/neon.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/neon/neon.min.css',
],
'options' => [],
],
'aurora' => [
'scripts' => ['/vendor/flasher/themes/aurora/aurora.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/aurora/aurora.min.css',
],
'options' => [],
],
'minimal' => [
'scripts' => ['/vendor/flasher/themes/minimal/minimal.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/minimal/minimal.min.css',
],
'options' => [],
],
'material' => [
'scripts' => ['/vendor/flasher/themes/material/material.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/material/material.min.css',
],
'options' => [],
],
'google' => [
'scripts' => ['/vendor/flasher/themes/google/google.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/google/google.min.css',
],
'options' => [],
],
'ios' => [
'scripts' => ['/vendor/flasher/themes/ios/ios.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/ios/ios.min.css',
],
'options' => [],
],
'slack' => [
'scripts' => ['/vendor/flasher/themes/slack/slack.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/slack/slack.min.css',
],
'options' => [],
],
'facebook' => [
'scripts' => ['/vendor/flasher/themes/facebook/facebook.min.js'],
'styles' => [
'/vendor/flasher/flasher.min.css',
'/vendor/flasher/themes/facebook/facebook.min.css',
],
'options' => [],
],
],
'translate' => true,
'inject_assets' => true,
'excluded_paths' => [