diff --git a/.github/workflows/monorepo-split-packages.yml b/.github/workflows/monorepo-split-packages.yml index 83c72b6e4..2fab7ad51 100644 --- a/.github/workflows/monorepo-split-packages.yml +++ b/.github/workflows/monorepo-split-packages.yml @@ -64,6 +64,7 @@ jobs: - press - product - progress + - prompts - publish - record - restore diff --git a/packages/devlink/config/devlink.php b/packages/devlink/config/devlink.php index 9a72324d1..2576679a4 100644 --- a/packages/devlink/config/devlink.php +++ b/packages/devlink/config/devlink.php @@ -338,6 +338,11 @@ 'path' => $public_base_path.'/progress', 'type' => 'public', ], + 'prompts' => [ + 'active' => true, + 'path' => $public_base_path.'/prompts', + 'type' => 'public', + ], 'record' => [ 'active' => false, 'path' => $public_base_path.'/record', diff --git a/packages/prompts/CHANGELOG.md b/packages/prompts/CHANGELOG.md new file mode 100644 index 000000000..d806cfe83 --- /dev/null +++ b/packages/prompts/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +We currently don't track changes in this package. Please refer to the [Moox Monorepo](https://github.com/mooxphp/moox) for the latest changes. diff --git a/packages/prompts/LICENSE.md b/packages/prompts/LICENSE.md new file mode 100644 index 000000000..7dfc5ad0b --- /dev/null +++ b/packages/prompts/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Moox + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/prompts/README.md b/packages/prompts/README.md new file mode 100644 index 000000000..2507e6b79 --- /dev/null +++ b/packages/prompts/README.md @@ -0,0 +1,60 @@ +# Moox Prompts + +CLI-kompatible Prompts für Laravel Artisan Commands. + +## Übersicht + +Dieses Package bietet eine einfache Proxy-Implementierung für Laravel Prompts. Es ermöglicht es, die gleichen Helper-Funktionen wie Laravel Prompts zu verwenden, mit der Möglichkeit, später Web-Funktionalität hinzuzufügen. + +## Features + +- ✅ Alle Laravel Prompt-Typen unterstützt (`text`, `select`, `multiselect`, `confirm`, etc.) +- ✅ Identische API wie Laravel Prompts + +## Installation + +```bash +composer require moox/prompts +``` + +## Verwendung + +### In Commands + +Verwende die gleichen Helper-Funktionen wie in Laravel Prompts: + +```php +use function Moox\Prompts\text; +use function Moox\Prompts\select; +use function Moox\Prompts\confirm; +use function Moox\Prompts\form; + +public function handle() +{ + // Einzelne Prompts + $name = text('What is your name?'); + $package = select('Which package?', ['moox/core', 'moox/user']); + $confirm = confirm('Are you sure?'); + + // FormBuilder + $result = form() + ->text('Name?') + ->select('Package?', ['moox/core', 'moox/user']) + ->submit(); + + // Command-Logik... +} +``` + +## Architektur + +Das Package besteht aus: + +- **PromptRuntime**: Interface für Prompt-Implementierungen +- **CliPromptRuntime**: CLI-Implementierung (delegiert an Laravel Prompts) +- **functions.php**: Globale Helper-Funktionen +- **PromptsServiceProvider**: Registriert Services + +## License + +Siehe [LICENSE.md](LICENSE.md) diff --git a/packages/prompts/ROADMAP.md b/packages/prompts/ROADMAP.md new file mode 100644 index 000000000..5176076a6 --- /dev/null +++ b/packages/prompts/ROADMAP.md @@ -0,0 +1,5 @@ +# Roadmap + +## Current tasks + +- [ ] Create that package diff --git a/packages/prompts/SECURITY.md b/packages/prompts/SECURITY.md new file mode 100644 index 000000000..af1170db7 --- /dev/null +++ b/packages/prompts/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Supported Versions + +We maintain the current version of `Moox Prompts` actively. + +Do not expect security fixes for older versions. + +## Reporting a Vulnerability + +If you find any security-related bug, please report it to security@moox.org. + +Please do not use Github issues, to give us enough time to review and fix the issue, before others can use it, to do stupid things. diff --git a/packages/prompts/banner.jpg b/packages/prompts/banner.jpg new file mode 100644 index 000000000..bce090d23 Binary files /dev/null and b/packages/prompts/banner.jpg differ diff --git a/packages/prompts/composer.json b/packages/prompts/composer.json new file mode 100644 index 000000000..311c7a31f --- /dev/null +++ b/packages/prompts/composer.json @@ -0,0 +1,107 @@ +{ + "name": "moox/prompts", + "description": "Prompts is a simple Moox Entity, that can be used to create and manage simple entries, like logs.", + "keywords": [ + "Moox", + "Laravel", + "Filament", + "Moox package", + "Laravel package" + ], + "homepage": "https://moox.org/docs/prompts", + "license": "MIT", + "authors": [ + { + "name": "Moox Developer", + "email": "dev@moox.org", + "role": "Developer" + } + ], + "require": { + "moox/core": "*" + }, + "autoload": { + "psr-4": { + "Moox\\Prompts\\": "src", + "Moox\\Prompts\\Database\\Factories\\": "database/factories" + } + }, + "autoload-dev": { + "psr-4": { + "Moox\\Prompts\\Tests\\": "tests/", + "Workbench\\App\\": "workbench/app/", + "Workbench\\Database\\Factories\\": "workbench/database/factories/", + "Workbench\\Database\\Seeders\\": "workbench/database/seeders/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Moox\\Prompts\\PromptsServiceProvider" + ] + }, + "moox": { + "name": "Moox Prompts", + "stability": "stable", + "type": "moox-plugin", + "install": { + "auto_migrate": "database/migrations", + "auto_entities": { + "Prompts": true + }, + "auto_class": { + "Prompts": "Moox\\Prompts\\Prompts" + }, + "auto_run": { + "Run this": "php artisan run:this", + "Build the frontend": "npm run build", + "Clear the cache": "php artisan cache:clear" + }, + "auto_runhere": { + "Build the frontend": "npm run build" + } + }, + "update": { + "migrate": "database/migrations", + "merge": "moox-prompts-config" + }, + "uninstall": { + "migrate": "database/migrations", + "remove": "moox-prompts-config" + } + } + }, + "prefer-stable": true, + "require-dev": { + "moox/devtools": "^4.0", + "pestphp/pest-plugin-browser": "^4.0", + "pestphp/pest-plugin-livewire": "^4.0" + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } + }, + "scripts": { + "post-autoload-dump": [ + "@clear", + "@prepare" + ], + "clear": "@php vendor/bin/testbench package:purge-skeleton --ansi", + "prepare": "@php vendor/bin/testbench package:discover --ansi", + "build": "@php vendor/bin/testbench workbench:build --ansi", + "serve": [ + "Composer\\Config::disableProcessTimeout", + "@build", + "@php vendor/bin/testbench serve --ansi" + ], + "lint": [ + "@php vendor/bin/pint --ansi", + "@php vendor/bin/phpstan analyse --verbose --ansi" + ], + "test": [ + "@clear", + "@php vendor/bin/pest" + ] + } +} \ No newline at end of file diff --git a/packages/prompts/config/prompts.php b/packages/prompts/config/prompts.php new file mode 100644 index 000000000..425331ef9 --- /dev/null +++ b/packages/prompts/config/prompts.php @@ -0,0 +1,56 @@ + false, + + 'resources' => [ + 'item' => [ + 'single' => 'trans//item::item.item', + 'plural' => 'trans//item::item.items', + 'tabs' => [ + 'all' => [ + 'label' => 'trans//core::core.all', + 'icon' => 'gmdi-filter-list', + 'query' => [ + [ + 'field' => 'title', + 'operator' => '!=', + 'value' => null, + ], + ], + ], + ], + ], + ], + + 'relations' => [], + + /* + |-------------------------------------------------------------------------- + | Navigation + |-------------------------------------------------------------------------- + | + | The navigation group and sort of the Resource, + | and if the panel is enabled. + | + */ + 'auth' => [ + 'user' => 'Moox\\DevTools\\Models\\TestUser', + ], + 'navigation_group' => 'DEV', +]; diff --git a/packages/prompts/resources/lang/de/prompts.php b/packages/prompts/resources/lang/de/prompts.php new file mode 100644 index 000000000..a5e40ea3a --- /dev/null +++ b/packages/prompts/resources/lang/de/prompts.php @@ -0,0 +1,6 @@ + 'Prompt', + 'prompts' => 'Prompts', +]; diff --git a/packages/prompts/resources/lang/en/prompts.php b/packages/prompts/resources/lang/en/prompts.php new file mode 100644 index 000000000..a5e40ea3a --- /dev/null +++ b/packages/prompts/resources/lang/en/prompts.php @@ -0,0 +1,6 @@ + 'Prompt', + 'prompts' => 'Prompts', +]; diff --git a/packages/prompts/src/PromptsServiceProvider.php b/packages/prompts/src/PromptsServiceProvider.php new file mode 100644 index 000000000..170c19457 --- /dev/null +++ b/packages/prompts/src/PromptsServiceProvider.php @@ -0,0 +1,34 @@ +name('moox-prompts') + ->hasConfigFile('prompts'); + } + + public function register() + { + parent::register(); + + $this->app->singleton(PromptRuntime::class, function ($app) { + return new CliPromptRuntime; + }); + } + + public function boot(): void + { + parent::boot(); + + require_once __DIR__.'/functions.php'; + } +} diff --git a/packages/prompts/src/Support/CliPromptRuntime.php b/packages/prompts/src/Support/CliPromptRuntime.php new file mode 100644 index 000000000..b2756ea2d --- /dev/null +++ b/packages/prompts/src/Support/CliPromptRuntime.php @@ -0,0 +1,322 @@ + $label, + 'options' => $options, + 'placeholder' => $placeholder, + 'scroll' => $scroll !== null ? (int) $scroll : 5, + 'validate' => $validate, + 'hint' => $hint, + 'transform' => $transform, + ]; + + if ($required !== false) { + $params['required'] = $required; + } + + return Prompts\search(...$params); + } + + public function multisearch( + string $label, + Closure $options, + bool|string $required = false, + callable|string|array|null $validate = null, + string $placeholder = '', + ?string $scroll = null, + string $hint = '', + callable|string|null $transform = null, + ): array { + return Prompts\multisearch( + label: $label, + options: $options, + placeholder: $placeholder, + scroll: $scroll !== null ? (int) $scroll : 5, + required: $required, + validate: $validate, + hint: $hint, + transform: $transform, + ); + } + + /* + |-------------------------------------------------------------------------- + | Auxiliary Prompts + |-------------------------------------------------------------------------- + */ + + public function pause(string $message = 'Press ENTER to continue'): void + { + Prompts\pause($message); + } + + /* + |-------------------------------------------------------------------------- + | Informational + |-------------------------------------------------------------------------- + */ + + public function note(string $message): void + { + Prompts\note($message); + } + + public function info(string $message): void + { + Prompts\info($message); + } + + public function warning(string $message): void + { + Prompts\warning($message); + } + + public function error(string $message): void + { + Prompts\error($message); + } + + public function alert(string $message): void + { + Prompts\alert($message); + } + + public function intro(string $message): void + { + Prompts\intro($message); + } + + public function outro(string $message): void + { + Prompts\outro($message); + } + + /* + |-------------------------------------------------------------------------- + | Table Output + |-------------------------------------------------------------------------- + */ + + public function table(array $headers, array $rows): void + { + Prompts\table($headers, $rows); + } + + /* + |-------------------------------------------------------------------------- + | Spinner + |-------------------------------------------------------------------------- + */ + + public function spin( + Closure $callback, + string $message = '', + ): mixed { + return Prompts\spin($callback, $message); + } + + /* + |-------------------------------------------------------------------------- + | Progress + |-------------------------------------------------------------------------- + */ + + public function progress( + string $label, + iterable|int $steps, + ?Closure $callback = null, + string $hint = '', + ): Progress|array { + return Prompts\progress( + label: $label, + steps: $steps, + callback: $callback, + hint: $hint, + ); + } + + /* + |-------------------------------------------------------------------------- + | Clear Terminal + |-------------------------------------------------------------------------- + */ + + public function clear(): void + { + Prompts\clear(); + } + + /* + |-------------------------------------------------------------------------- + | Form + |-------------------------------------------------------------------------- + */ + + public function form(): FormBuilder + { + return Prompts\form(); + } +} diff --git a/packages/prompts/src/Support/PromptRuntime.php b/packages/prompts/src/Support/PromptRuntime.php new file mode 100644 index 000000000..f2582899c --- /dev/null +++ b/packages/prompts/src/Support/PromptRuntime.php @@ -0,0 +1,171 @@ +text( + $label, $placeholder, $default, $required, + $validate, $hint, $transform + ); +} + +function textarea( + string $label, + string $placeholder = '', + bool|string $required = false, + callable|string|array|null $validate = null, + string $hint = '', + callable|string|null $transform = null, +): string { + return app(PromptRuntime::class)->textarea( + $label, $placeholder, $required, $validate, $hint, $transform + ); +} + +function password( + string $label, + string $placeholder = '', + bool|string $required = false, + callable|string|array|null $validate = null, + string $hint = '', + callable|string|null $transform = null, +): string { + return app(PromptRuntime::class)->password( + $label, $placeholder, $required, $validate, $hint, $transform + ); +} + +function confirm( + string $label, + bool $default = false, + bool|string $required = false, + string $yes = 'I accept', + string $no = 'I decline', + string $hint = '', +): bool { + return app(PromptRuntime::class)->confirm( + $label, $default, $required, $yes, $no, $hint + ); +} + +function select( + string $label, + array $options, + mixed $default = null, + ?string $scroll = null, + string $hint = '', + callable|string|array|null $validate = null, + callable|string|null $transform = null, +): mixed { + return app(PromptRuntime::class)->select( + $label, $options, $default, + $scroll, $hint, $validate, $transform + ); +} + +function multiselect( + string $label, + array $options, + array $default = [], + bool|string $required = false, + ?string $scroll = null, + string $hint = '', + callable|string|array|null $validate = null, + callable|string|null $transform = null, +): array { + return app(PromptRuntime::class)->multiselect( + $label, $options, $default, $required, + $scroll, $hint, $validate, $transform + ); +} + +function suggest( + string $label, + array|Closure $options, + mixed $default = null, + bool|string $required = false, + callable|string|array|null $validate = null, + string $placeholder = '', + string $hint = '', + callable|string|null $transform = null, +): mixed { + return app(PromptRuntime::class)->suggest( + $label, $options, $default, $required, + $validate, $placeholder, $hint, $transform + ); +} + +function search( + string $label, + Closure $options, + bool|string $required = false, + callable|string|array|null $validate = null, + string $placeholder = '', + ?string $scroll = null, + string $hint = '', + callable|string|null $transform = null, +): mixed { + return app(PromptRuntime::class)->search( + $label, $options, $required, + $validate, $placeholder, $scroll, $hint, $transform + ); +} + +function multisearch( + string $label, + Closure $options, + bool|string $required = false, + callable|string|array|null $validate = null, + string $placeholder = '', + ?string $scroll = null, + string $hint = '', + callable|string|null $transform = null, +): array { + return app(PromptRuntime::class)->multisearch( + $label, $options, $required, + $validate, $placeholder, $scroll, $hint, $transform + ); +} + +/* +|-------------------------------------------------------------------------- +| Auxiliary Prompts +|-------------------------------------------------------------------------- +*/ + +function pause(string $message = 'Press ENTER to continue'): void +{ + app(PromptRuntime::class)->pause($message); +} + +/* +|-------------------------------------------------------------------------- +| Informational +|-------------------------------------------------------------------------- +*/ + +function note(string $message): void +{ + app(PromptRuntime::class)->note($message); +} + +function info(string $message): void +{ + app(PromptRuntime::class)->info($message); +} + +function warning(string $message): void +{ + app(PromptRuntime::class)->warning($message); +} + +function error(string $message): void +{ + app(PromptRuntime::class)->error($message); +} + +function alert(string $message): void +{ + app(PromptRuntime::class)->alert($message); +} + +function intro(string $message): void +{ + app(PromptRuntime::class)->intro($message); +} + +function outro(string $message): void +{ + app(PromptRuntime::class)->outro($message); +} + +/* +|-------------------------------------------------------------------------- +| Table +|-------------------------------------------------------------------------- +*/ + +function table(array $headers, array $rows): void +{ + app(PromptRuntime::class)->table($headers, $rows); +} + +/* +|-------------------------------------------------------------------------- +| Spinner +|-------------------------------------------------------------------------- +*/ + +function spin(Closure $callback, string $message = ''): mixed +{ + return app(PromptRuntime::class)->spin($callback, $message); +} + +/* +|-------------------------------------------------------------------------- +| Progress +|-------------------------------------------------------------------------- +*/ + +function progress( + string $label, + iterable|int $steps, + ?Closure $callback = null, + string $hint = '', +): Progress|array { + return app(PromptRuntime::class)->progress( + $label, $steps, $callback, $hint + ); +} + +/* +|-------------------------------------------------------------------------- +| Clear +|-------------------------------------------------------------------------- +*/ + +function clear(): void +{ + app(PromptRuntime::class)->clear(); +} + +/* +|-------------------------------------------------------------------------- +| Form +|-------------------------------------------------------------------------- +*/ + +function form(): FormBuilder +{ + return app(PromptRuntime::class)->form(); +}