kaze 2.0.1 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -1
- data/README.md +42 -2
- data/lib/kaze/commands/app/base_stack.rb +25 -0
- data/lib/kaze/commands/app/hotwire_stack.rb +18 -0
- data/lib/kaze/commands/app/inertia_react_stack.rb +23 -0
- data/lib/kaze/commands/app/inertia_vue_stack.rb +24 -0
- data/lib/kaze/commands/app.rb +49 -4
- data/lib/kaze/version.rb +1 -1
- data/stubs/hotwire/app/views/auth/forgot_password.html.erb +1 -1
- data/stubs/hotwire/app/views/auth/login.html.erb +1 -1
- data/stubs/hotwire/app/views/auth/register.html.erb +2 -2
- data/stubs/hotwire/app/views/auth/reset_password.html.erb +3 -3
- data/stubs/hotwire/app/views/auth/verify_email.html.erb +2 -2
- data/stubs/hotwire/app/views/layouts/_sidebar_user.html.erb +1 -1
- data/stubs/hotwire/app/views/settings/password/_update_password_form.html.erb +6 -6
- data/stubs/hotwire/app/views/settings/profile/_confirm_user_deletion_modal.html.erb +1 -1
- data/stubs/hotwire/app/views/settings/profile/_delete_user_form.html.erb +1 -1
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +2 -2
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +1 -1
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +3 -3
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/VerifyEmail.tsx +3 -3
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Partials/DeleteUserForm.tsx +2 -2
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Partials/UpdatePasswordForm.tsx +3 -3
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +2 -2
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +1 -1
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +3 -3
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/VerifyEmail.vue +3 -3
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Partials/DeleteUserForm.vue +2 -2
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Partials/UpdatePasswordForm.vue +3 -3
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55a910134ce65d8e8401e8093507fc565efaf81d54df89873d5541cb51214b1b
|
|
4
|
+
data.tar.gz: b6d8afe1350896e1fb047d753d7271204e1ffd8ba1127504311799af0704deeb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7692a5a56a442c9dc100ee7236e1c611b6ce3d53a03b012386f308b0179780864c395ddcc56fb15cb92c013bc5be11aeef7bd4a3e1eabb2209731cb635da73fc
|
|
7
|
+
data.tar.gz: 46e3659514c1f9820f01aa89a71be012a41efbdccc79861f035b75565ade82557e6a0b3b0f1a4d657cdccd94d0ff181e333fe9ff0e0f1918b2a65344a135c54d
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,38 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.1.1] - 2026-10-25
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- User-visible text now reads the same across the three stacks and within a page. Hotwire's login button said "Login" where React and Vue said "Log in", which is what prompted this — but the sweep found the real problem was that the 2.0.0 settings area introduced sentence case ("Profile information", "Delete account") while the auth pages kept the title case inherited from Breeze ("Reset Password", "Confirm Password"), so the two conventions met inside the same screen. The delete-account page had a "Delete account" heading directly above a "Delete Account" button.
|
|
14
|
+
|
|
15
|
+
Everything settles on sentence case, matching the settings area and Laravel's current kits: "Log in", "Log out", "Reset password", "Confirm password", "Current password", "New password", "Delete account", "Email password reset link", "Resend verification email", and the "Forgot password" and "Email verification" page titles. Mailer copy is left in title case — a different surface, and Laravel's reset email keeps it too.
|
|
16
|
+
|
|
17
|
+
### Documentation
|
|
18
|
+
|
|
19
|
+
- Screenshots and badges in the README. A starter kit whose selling point is what it looks like had no picture of itself anywhere — someone evaluating Kaze had to install it to find out. Three captures of the real scaffolded application cover the sidebar shell, the settings area and both themes, at 48 KB in total and outside the gem's packaged files.
|
|
20
|
+
|
|
21
|
+
- A README section on the frontend. The 2.0.0 redesign is the largest change this project has had and the README described none of it — what shadcn/ui means here, that the primitives are yours to edit, how to add more with `npx shadcn@latest add`, the capitalised `@/Components` alias that will bite anyone hand-writing an import, the token and appearance systems, and why the Hotwire stack hand-rolls its own equivalents.
|
|
22
|
+
|
|
23
|
+
### Internal
|
|
24
|
+
|
|
25
|
+
- A test suite for the gem itself, run by `rake test` and by a new `Gem Tests` job in CI. Until now the only automated coverage was the scaffolded applications' own tests, which exercise the stubs but never the code that copies them.
|
|
26
|
+
|
|
27
|
+
The bulk of it covers `kaze upgrade`, since that is the part that deletes files from a real application: that a directory is pruned only once empty, that one still holding someone else's work survives along with the file in it, that nothing above `app/` is ever touched, and that a path missing from the application is skipped rather than raised on. The rest pins the removal manifests — no listed path may still be shipped by the stubs, which would have `upgrade` delete a file the install puts straight back — and checks that `lib/kaze/version.rb` matches the newest changelog heading, so a release cannot ship a version the changelog does not describe.
|
|
28
|
+
|
|
29
|
+
The tests are excluded from the built gem.
|
|
30
|
+
|
|
31
|
+
## [2.1.0] - 2026-10-22
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- A `kaze upgrade [STACK]` command. Kaze copies files into an application and does not own them afterwards, so `kaze install` cannot tell a leftover from an older version apart from something you wrote, and has always left both alone. `upgrade` resolves that by asking instead of guessing: it lists the files an earlier Kaze installed that this version no longer ships, waits for you to agree, removes them, and then runs the normal install.
|
|
36
|
+
|
|
37
|
+
Only paths Kaze itself shipped are ever offered — the list per stack is fixed in the source, so nothing you added alongside them can be caught by it, and a directory is removed only once it is empty. `--dry-run` prints the list and stops; `--force` skips the prompt for scripts. Declining exits non-zero and changes nothing.
|
|
38
|
+
|
|
39
|
+
This replaces the manual file list in the 2.0.0 upgrade notes below. On the React stack it is the difference between an upgraded application building and not: `tsc` typechecks every file under `app/javascript`, so the old `Pages/Profile/Edit.tsx` fails the build until it is gone.
|
|
40
|
+
|
|
9
41
|
## [2.0.1] - 2026-10-22
|
|
10
42
|
|
|
11
43
|
### Fixed
|
|
@@ -185,7 +217,9 @@ Together these take a freshly scaffolded Inertia application from seven npm advi
|
|
|
185
217
|
|
|
186
218
|
Releases before 1.0.0 are not catalogued here. See the [tags](https://github.com/cuonggt/kaze/tags) for their history.
|
|
187
219
|
|
|
188
|
-
[Unreleased]: https://github.com/cuonggt/kaze/compare/v2.
|
|
220
|
+
[Unreleased]: https://github.com/cuonggt/kaze/compare/v2.1.1...HEAD
|
|
221
|
+
[2.1.1]: https://github.com/cuonggt/kaze/compare/v2.1.0...v2.1.1
|
|
222
|
+
[2.1.0]: https://github.com/cuonggt/kaze/compare/v2.0.1...v2.1.0
|
|
189
223
|
[2.0.1]: https://github.com/cuonggt/kaze/compare/v2.0.0...v2.0.1
|
|
190
224
|
[2.0.0]: https://github.com/cuonggt/kaze/compare/v1.4.0...v2.0.0
|
|
191
225
|
[1.4.0]: https://github.com/cuonggt/kaze/compare/v1.3.1...v1.4.0
|
data/README.md
CHANGED
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
# Kaze
|
|
4
4
|
|
|
5
|
+
[](https://rubygems.org/gems/kaze)
|
|
6
|
+
[](https://github.com/cuonggt/kaze/actions/workflows/tests.yml)
|
|
7
|
+
[](LICENSE.md)
|
|
8
|
+
|
|
5
9
|
Heavily inspired by [Laravel Breeze](https://github.com/laravel/breeze), this gem offers authentication and application starter kits to give you a head start building your new [Rails](https://rubyonrails.org) application. These kits automatically scaffold your application with the routes, controllers, and views you need to register and authenticate your application's users.
|
|
6
10
|
|
|
7
|
-
[Kaze](https://github.com/cuonggt/kaze) is
|
|
11
|
+
[Kaze](https://github.com/cuonggt/kaze) is an opinionated, minimal, simple implementation of all authentication features that a modern web application should have, including login, registration, password reset, and email verification. In addition, Kaze includes a settings area where the user may update their name and email address, change their password, choose an appearance, and delete their account.
|
|
8
12
|
|
|
9
13
|
Kaze provides scaffolding options based on [Hotwire](https://hotwired.dev) or [Inertia](https://inertiajs.com), with the choice of using Vue or React for the Inertia-based scaffolding.
|
|
10
14
|
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
| | |
|
|
18
|
+
|---|---|
|
|
19
|
+
|  |  |
|
|
20
|
+
|
|
21
|
+
The three stacks are built to the same design, so these are from the React one.
|
|
22
|
+
|
|
11
23
|
## Installation
|
|
12
24
|
|
|
13
25
|
Before creating your first project powered by Kaze, make sure that your local machine has Ruby installed. Ruby can be installed in minutes via [mise](https://mise.jdx.dev).
|
|
@@ -36,7 +48,7 @@ Once the project has been created, you may scaffold your application using one o
|
|
|
36
48
|
|
|
37
49
|
## Kaze and Hotwire
|
|
38
50
|
|
|
39
|
-
The default Kaze "stack" is the Hotwire stack. Hotwire is a powerful way to
|
|
51
|
+
The default Kaze "stack" is the Hotwire stack. Hotwire is a powerful way to build dynamic, reactive, front-end UIs primarily using Ruby and ERB templates without using much JavaScript by sending HTML instead of JSON over the wire.
|
|
40
52
|
|
|
41
53
|
The Hotwire stack may be installed by invoking the `kaze install` command with no other additional arguments inside your app directory. This command publishes the authentication, views, routes, controllers, and other resources to your application.
|
|
42
54
|
|
|
@@ -75,3 +87,31 @@ bin/dev
|
|
|
75
87
|
```
|
|
76
88
|
|
|
77
89
|
Next, you may navigate to your application's `/login` or `/register` URLs in your web browser. All of Kaze's routes are defined within the `config/routes.rb` file.
|
|
90
|
+
|
|
91
|
+
## The frontend
|
|
92
|
+
|
|
93
|
+
Both Inertia stacks are built on [shadcn/ui](https://ui.shadcn.com) — thirteen primitives apiece (button, input, label, checkbox, dialog, dropdown menu, separator, skeleton, sheet, tooltip, avatar, breadcrumb and sidebar), on Radix for React and [reka-ui](https://reka-ui.com) for Vue. shadcn copies source into a project rather than shipping a package, which is how Kaze already works, so these land in `app/javascript/Components/ui` and are yours to change.
|
|
94
|
+
|
|
95
|
+
A `components.json` comes with them, so you can add more:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
npx shadcn@latest add card
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
One thing to know if you reach for that: Kaze uses a capitalised `@/Components`, which `components.json` already records. The CLI will follow it, but an import you write by hand as `@/components/ui/card` will resolve on macOS and fail on Linux.
|
|
102
|
+
|
|
103
|
+
Colour runs through design tokens — `bg-background`, `text-muted-foreground`, `border-sidebar-border` and so on, defined once per stylesheet as oklch variables — rather than per-element `dark:` pairs. Appearance is a user preference of light, dark or system, stored in `localStorage` and applied before first paint so the page never flashes the wrong theme; system follows the device and tracks it live. The application layout is a sidebar that collapses to icons and becomes a drawer on small screens, remembering its state in a cookie the server reads back.
|
|
104
|
+
|
|
105
|
+
There is no shadcn for Hotwire. That stack gets the same look from ViewComponents written against the same tokens, with the handful of [lucide](https://lucide.dev) icons the layouts need inlined into an `IconComponent`, since it carries no npm dependencies at all.
|
|
106
|
+
|
|
107
|
+
## Upgrading
|
|
108
|
+
|
|
109
|
+
Kaze copies files into your application and does not own them afterwards, so re-running `kaze install` cannot know whether a file it no longer ships was left over from an older version or written by you. `kaze upgrade` resolves that by asking:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
kaze upgrade react
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
It lists the files an earlier Kaze installed that this version no longer ships, waits for you to agree, removes them, and then runs the normal install. Only files Kaze itself shipped are ever listed — anything you added alongside them is untouched, and a directory is removed only once it is empty. Pass `--dry-run` to see the list and stop, or `--force` to skip the prompt in a script.
|
|
116
|
+
|
|
117
|
+
Upgrading from 1.x to 2.x matters on the React stack in particular: `tsc` typechecks every file under `app/javascript`, so the old `Pages/Profile/Edit.tsx` fails the build until it is gone.
|
|
@@ -7,8 +7,33 @@ class Kaze::Commands::App::BaseStack
|
|
|
7
7
|
raise NotImplementedError
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
# Paths from this stack's REMOVED_PATHS that the application still has. Only
|
|
11
|
+
# ever a subset of that list: `kaze upgrade` never touches a path Kaze did not
|
|
12
|
+
# put there itself.
|
|
13
|
+
def removable_paths
|
|
14
|
+
self.class::REMOVED_PATHS.select { |path| File.exist?("#{Dir.pwd}/#{path}") }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def remove_paths(paths)
|
|
18
|
+
paths.each { |path| FileUtils.rm_f("#{Dir.pwd}/#{path}") }
|
|
19
|
+
|
|
20
|
+
prune_empty_directories(paths)
|
|
21
|
+
end
|
|
22
|
+
|
|
10
23
|
private
|
|
11
24
|
|
|
25
|
+
# Deleting the last file in a directory leaves the directory behind. Walk back
|
|
26
|
+
# up from each one removing those that are now empty, and stop at the first
|
|
27
|
+
# that is not -- a directory with anything left in it is the application's.
|
|
28
|
+
def prune_empty_directories(paths)
|
|
29
|
+
paths.map { |path| File.dirname("#{Dir.pwd}/#{path}") }.uniq.sort_by(&:length).reverse.each do |dir|
|
|
30
|
+
while dir.start_with?("#{Dir.pwd}/app/") && Dir.exist?(dir) && Dir.empty?(dir)
|
|
31
|
+
Dir.rmdir(dir)
|
|
32
|
+
dir = File.dirname(dir)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
12
37
|
def bundle_command
|
|
13
38
|
@bundle_command ||= File.exist?("#{Dir.pwd}/bin/bundle") ? "#{Dir.pwd}/bin/bundle" : 'bundle'
|
|
14
39
|
end
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
class Kaze::Commands::App::HotwireStack < Kaze::Commands::App::BaseStack
|
|
2
|
+
# Paths that earlier versions of Kaze installed here and this version no longer
|
|
3
|
+
# ships. `kaze upgrade` offers to delete whichever of them an application still
|
|
4
|
+
# has; anything removed in a future version belongs on this list too.
|
|
5
|
+
REMOVED_PATHS = [
|
|
6
|
+
'app/controllers/profile_controller.rb',
|
|
7
|
+
'app/controllers/password_controller.rb',
|
|
8
|
+
'app/components/nav_link_component.rb',
|
|
9
|
+
'app/components/responsive_nav_link_component.rb',
|
|
10
|
+
'app/views/layouts/_navigation.html.erb',
|
|
11
|
+
'app/views/profile/destroy.turbo_stream.erb',
|
|
12
|
+
'app/views/profile/edit.html.erb',
|
|
13
|
+
'app/views/profile/partials/_confirm_user_deletion_modal.html.erb',
|
|
14
|
+
'app/views/profile/partials/_delete_user_form.html.erb',
|
|
15
|
+
'app/views/profile/partials/_update_appearance_form.html.erb',
|
|
16
|
+
'app/views/profile/partials/_update_password_form.html.erb',
|
|
17
|
+
'app/views/profile/partials/_update_profile_information_form.html.erb'
|
|
18
|
+
].freeze
|
|
19
|
+
|
|
2
20
|
def install
|
|
3
21
|
# Gems...
|
|
4
22
|
return unless remove_gems([ 'sprockets-rails', 'stimulus-rails' ])
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
class Kaze::Commands::App::InertiaReactStack < Kaze::Commands::App::BaseStack
|
|
2
|
+
# Paths that earlier versions of Kaze installed here and this version no longer
|
|
3
|
+
# ships. `kaze upgrade` offers to delete whichever of them an application still
|
|
4
|
+
# has; anything removed in a future version belongs on this list too.
|
|
5
|
+
REMOVED_PATHS = [
|
|
6
|
+
'app/controllers/profile_controller.rb',
|
|
7
|
+
'app/controllers/password_controller.rb',
|
|
8
|
+
'app/javascript/Components/Checkbox.tsx',
|
|
9
|
+
'app/javascript/Components/DangerButton.tsx',
|
|
10
|
+
'app/javascript/Components/Dropdown.tsx',
|
|
11
|
+
'app/javascript/Components/InputLabel.tsx',
|
|
12
|
+
'app/javascript/Components/Modal.tsx',
|
|
13
|
+
'app/javascript/Components/NavLink.tsx',
|
|
14
|
+
'app/javascript/Components/PrimaryButton.tsx',
|
|
15
|
+
'app/javascript/Components/ResponsiveNavLink.tsx',
|
|
16
|
+
'app/javascript/Components/SecondaryButton.tsx',
|
|
17
|
+
'app/javascript/Components/TextInput.tsx',
|
|
18
|
+
'app/javascript/Pages/Profile/Edit.tsx',
|
|
19
|
+
'app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx',
|
|
20
|
+
'app/javascript/Pages/Profile/Partials/UpdateAppearanceForm.tsx',
|
|
21
|
+
'app/javascript/Pages/Profile/Partials/UpdatePasswordForm.tsx',
|
|
22
|
+
'app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.tsx'
|
|
23
|
+
].freeze
|
|
24
|
+
|
|
2
25
|
def install
|
|
3
26
|
# Gems...
|
|
4
27
|
return unless remove_gems([ 'sprockets-rails', 'turbo-rails', 'stimulus-rails' ])
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
class Kaze::Commands::App::InertiaVueStack < Kaze::Commands::App::BaseStack
|
|
2
|
+
# Paths that earlier versions of Kaze installed here and this version no longer
|
|
3
|
+
# ships. `kaze upgrade` offers to delete whichever of them an application still
|
|
4
|
+
# has; anything removed in a future version belongs on this list too.
|
|
5
|
+
REMOVED_PATHS = [
|
|
6
|
+
'app/controllers/profile_controller.rb',
|
|
7
|
+
'app/controllers/password_controller.rb',
|
|
8
|
+
'app/javascript/Components/Checkbox.vue',
|
|
9
|
+
'app/javascript/Components/DangerButton.vue',
|
|
10
|
+
'app/javascript/Components/Dropdown.vue',
|
|
11
|
+
'app/javascript/Components/DropdownLink.vue',
|
|
12
|
+
'app/javascript/Components/InputLabel.vue',
|
|
13
|
+
'app/javascript/Components/Modal.vue',
|
|
14
|
+
'app/javascript/Components/NavLink.vue',
|
|
15
|
+
'app/javascript/Components/PrimaryButton.vue',
|
|
16
|
+
'app/javascript/Components/ResponsiveNavLink.vue',
|
|
17
|
+
'app/javascript/Components/SecondaryButton.vue',
|
|
18
|
+
'app/javascript/Components/TextInput.vue',
|
|
19
|
+
'app/javascript/Pages/Profile/Edit.vue',
|
|
20
|
+
'app/javascript/Pages/Profile/Partials/DeleteUserForm.vue',
|
|
21
|
+
'app/javascript/Pages/Profile/Partials/UpdateAppearanceForm.vue',
|
|
22
|
+
'app/javascript/Pages/Profile/Partials/UpdatePasswordForm.vue',
|
|
23
|
+
'app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.vue'
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
2
26
|
def install
|
|
3
27
|
# Gems...
|
|
4
28
|
return unless remove_gems([ 'sprockets-rails', 'turbo-rails', 'stimulus-rails' ])
|
data/lib/kaze/commands/app.rb
CHANGED
|
@@ -7,10 +7,7 @@ class Kaze::Commands::App < Thor
|
|
|
7
7
|
|
|
8
8
|
desc 'install [STACK]', 'Install the Kaze controllers and resources. Supported stacks: hotwire, react, vue.'
|
|
9
9
|
def install(stack = 'hotwire')
|
|
10
|
-
|
|
11
|
-
say 'Kaze must be run in a new Rails application.', :red
|
|
12
|
-
exit 1
|
|
13
|
-
end
|
|
10
|
+
ensure_rails_application
|
|
14
11
|
|
|
15
12
|
# The stack installers stop at the first step that fails, so a falsy return
|
|
16
13
|
# means the application is half-installed. Exiting non-zero keeps that from
|
|
@@ -21,8 +18,56 @@ class Kaze::Commands::App < Thor
|
|
|
21
18
|
exit 1
|
|
22
19
|
end
|
|
23
20
|
|
|
21
|
+
desc 'upgrade [STACK]', 'Remove files an earlier Kaze installed that this version no longer ships, then install.'
|
|
22
|
+
method_option :force, type: :boolean, default: false, aliases: '-f', desc: 'Skip the confirmation prompt.'
|
|
23
|
+
method_option :'dry-run', type: :boolean, default: false, desc: 'List what would be removed and stop.'
|
|
24
|
+
def upgrade(stack = 'hotwire')
|
|
25
|
+
ensure_rails_application
|
|
26
|
+
|
|
27
|
+
stack = StackFactory.make(stack.to_sym)
|
|
28
|
+
paths = stack.removable_paths
|
|
29
|
+
|
|
30
|
+
if paths.empty?
|
|
31
|
+
say 'Nothing to remove. This application has none of the files earlier versions installed.', :green
|
|
32
|
+
else
|
|
33
|
+
say "Kaze no longer ships #{paths.length} #{paths.length == 1 ? 'file' : 'files'} this application still has:", :yellow
|
|
34
|
+
say ''
|
|
35
|
+
paths.each { |path| say " #{path}" }
|
|
36
|
+
say ''
|
|
37
|
+
say 'They are yours now, so check for your own changes before agreeing.', :yellow
|
|
38
|
+
|
|
39
|
+
return if options[:'dry-run']
|
|
40
|
+
|
|
41
|
+
unless options[:force] || yes?('Remove them? [y/N]')
|
|
42
|
+
say 'Nothing was removed.', :red
|
|
43
|
+
exit 1
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
stack.remove_paths(paths)
|
|
47
|
+
|
|
48
|
+
say ''
|
|
49
|
+
say "Removed #{paths.length} #{paths.length == 1 ? 'file' : 'files'}.", :green
|
|
50
|
+
say ''
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
return if options[:'dry-run']
|
|
54
|
+
|
|
55
|
+
exit 1 unless stack.install
|
|
56
|
+
rescue => e
|
|
57
|
+
say e.message, :red
|
|
58
|
+
exit 1
|
|
59
|
+
end
|
|
60
|
+
|
|
24
61
|
desc 'version', 'Show Kaze version'
|
|
25
62
|
def version
|
|
26
63
|
puts Kaze::VERSION
|
|
27
64
|
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
def ensure_rails_application
|
|
68
|
+
return if File.exist?("#{Dir.pwd}/bin/rails")
|
|
69
|
+
|
|
70
|
+
say 'Kaze must be run in a new Rails application.', :red
|
|
71
|
+
exit 1
|
|
72
|
+
end
|
|
28
73
|
end
|
data/lib/kaze/version.rb
CHANGED
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
<%= render(TextInputComponent.new({ id: "password", class: "block mt-1 w-full", type: "password", name: "password", required: true, autocomplete: "new-password" })) %>
|
|
18
18
|
<%= render(InputErrorComponent.new({ class: "mt-2", message: @form.error_messages[:password] })) %>
|
|
19
19
|
</div>
|
|
20
|
-
<!-- Confirm
|
|
20
|
+
<!-- Confirm password -->
|
|
21
21
|
<div class="mt-4">
|
|
22
|
-
<%= render(InputLabelComponent.new({ for: "password_confirmation", value: "Confirm
|
|
22
|
+
<%= render(InputLabelComponent.new({ for: "password_confirmation", value: "Confirm password" })) %>
|
|
23
23
|
<%= render(TextInputComponent.new({ id: "password_confirmation", class: "block mt-1 w-full", type: "password", name: "password_confirmation", required: true, autocomplete: "new-password" })) %>
|
|
24
24
|
<%= render(InputErrorComponent.new({ class: "mt-2", message: @form.error_messages[:password_confirmation] })) %>
|
|
25
25
|
</div>
|
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
<%= render(TextInputComponent.new({ id: "password", class: "block mt-1 w-full", type: "password", name: "password", required: true, autofocus: true, autocomplete: "new-password" })) %>
|
|
8
8
|
<%= render(InputErrorComponent.new({ class: "mt-2", message: @form.error_messages[:password] })) %>
|
|
9
9
|
</div>
|
|
10
|
-
<!-- Confirm
|
|
10
|
+
<!-- Confirm password -->
|
|
11
11
|
<div class="mt-4">
|
|
12
|
-
<%= render(InputLabelComponent.new({ for: "password_confirmation", value: "Confirm
|
|
12
|
+
<%= render(InputLabelComponent.new({ for: "password_confirmation", value: "Confirm password" })) %>
|
|
13
13
|
<%= render(TextInputComponent.new({ id: "password_confirmation", class: "block mt-1 w-full", type: "password", name: "password_confirmation", required: true, autocomplete: "new-password" })) %>
|
|
14
14
|
<%= render(InputErrorComponent.new({ class: "mt-2", message: @form.error_messages[:password_confirmation] })) %>
|
|
15
15
|
</div>
|
|
16
16
|
<div class="flex items-center justify-end mt-4">
|
|
17
17
|
<%= render(PrimaryButtonComponent.new) do %>
|
|
18
|
-
Reset
|
|
18
|
+
Reset password
|
|
19
19
|
<% end %>
|
|
20
20
|
</div>
|
|
21
21
|
<% end %>
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
<form method="POST" action="<%= verification_send_path %>">
|
|
11
11
|
<div>
|
|
12
12
|
<%= render(PrimaryButtonComponent.new) do %>
|
|
13
|
-
Resend
|
|
13
|
+
Resend verification email
|
|
14
14
|
<% end %>
|
|
15
15
|
</div>
|
|
16
16
|
</form>
|
|
17
17
|
<form method="POST" action="<%= logout_path %>">
|
|
18
18
|
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
|
|
19
19
|
<button type="submit" class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800">
|
|
20
|
-
Log
|
|
20
|
+
Log out
|
|
21
21
|
</button>
|
|
22
22
|
</form>
|
|
23
23
|
</div>
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
|
|
29
29
|
<%= render(DropdownLinkComponent.new({ href: logout_path, onclick: "event.preventDefault();this.closest('form').submit()" })) do %>
|
|
30
30
|
<%= render(IconComponent.new('log-out')) %>
|
|
31
|
-
Log
|
|
31
|
+
Log out
|
|
32
32
|
<% end %>
|
|
33
33
|
</form>
|
|
34
34
|
<% end %>
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
<%= turbo_frame_tag 'update_password_form' do %>
|
|
2
2
|
<section>
|
|
3
3
|
<%= form_with model: @update_password_form, url: password_update_path, method: "put", class: "mt-6 space-y-6" do |update_password_form| %>
|
|
4
|
-
<!-- Current
|
|
4
|
+
<!-- Current password -->
|
|
5
5
|
<div>
|
|
6
|
-
<%= render(InputLabelComponent.new({ for: "update_password_current_password", value: "Current
|
|
6
|
+
<%= render(InputLabelComponent.new({ for: "update_password_current_password", value: "Current password" })) %>
|
|
7
7
|
<%= render(TextInputComponent.new({ id: "update_password_current_password", class: "block mt-1 w-full", type: "password", name: "current_password", autocomplete: "current-password" })) %>
|
|
8
8
|
<%= render(InputErrorComponent.new({ class: "mt-2", message: @update_password_form.error_messages[:current_password] })) %>
|
|
9
9
|
</div>
|
|
10
|
-
<!-- New
|
|
10
|
+
<!-- New password -->
|
|
11
11
|
<div>
|
|
12
|
-
<%= render(InputLabelComponent.new({ for: "update_password_password", value: "New
|
|
12
|
+
<%= render(InputLabelComponent.new({ for: "update_password_password", value: "New password" })) %>
|
|
13
13
|
<%= render(TextInputComponent.new({ id: "update_password_password", class: "block mt-1 w-full", type: "password", name: "password", autocomplete: "new-password" })) %>
|
|
14
14
|
<%= render(InputErrorComponent.new({ class: "mt-2", message: @update_password_form.error_messages[:password] })) %>
|
|
15
15
|
</div>
|
|
16
|
-
<!-- Confirm
|
|
16
|
+
<!-- Confirm password -->
|
|
17
17
|
<div class="mt-4">
|
|
18
|
-
<%= render(InputLabelComponent.new({ for: "update_password_password_confirmation", value: "Confirm
|
|
18
|
+
<%= render(InputLabelComponent.new({ for: "update_password_password_confirmation", value: "Confirm password" })) %>
|
|
19
19
|
<%= render(TextInputComponent.new({ id: "update_password_password_confirmation", class: "block mt-1 w-full", type: "password", name: "password_confirmation", autocomplete: "new-password" })) %>
|
|
20
20
|
<%= render(InputErrorComponent.new({ class: "mt-2", message: @update_password_form.error_messages[:password_confirmation] })) %>
|
|
21
21
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<section class="space-y-6">
|
|
2
2
|
<%= render(DangerButtonComponent.new({"x-data": "", "x-on:click.prevent": "$dispatch('open-modal', 'confirm-user-deletion')"})) do %>
|
|
3
|
-
Delete
|
|
3
|
+
Delete account
|
|
4
4
|
<% end %>
|
|
5
5
|
</section>
|
|
6
6
|
<%= render 'settings/profile/confirm_user_deletion_modal' %>
|
|
@@ -19,7 +19,7 @@ export default function ForgotPassword({ status }: { status?: string }) {
|
|
|
19
19
|
|
|
20
20
|
return (
|
|
21
21
|
<GuestLayout>
|
|
22
|
-
<Head title="Forgot
|
|
22
|
+
<Head title="Forgot password" />
|
|
23
23
|
|
|
24
24
|
<div className="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
|
25
25
|
Forgot your password? No problem. Just let us know your email address and we will email you a password reset
|
|
@@ -43,7 +43,7 @@ export default function ForgotPassword({ status }: { status?: string }) {
|
|
|
43
43
|
|
|
44
44
|
<div className="flex items-center justify-end mt-4">
|
|
45
45
|
<Button className="ms-4" disabled={processing}>
|
|
46
|
-
Email
|
|
46
|
+
Email password reset link
|
|
47
47
|
</Button>
|
|
48
48
|
</div>
|
|
49
49
|
</form>
|
|
@@ -24,7 +24,7 @@ export default function ResetPassword({ token }: { token: string }) {
|
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
26
|
<GuestLayout>
|
|
27
|
-
<Head title="Reset
|
|
27
|
+
<Head title="Reset password" />
|
|
28
28
|
|
|
29
29
|
<form onSubmit={submit}>
|
|
30
30
|
<div>
|
|
@@ -45,7 +45,7 @@ export default function ResetPassword({ token }: { token: string }) {
|
|
|
45
45
|
</div>
|
|
46
46
|
|
|
47
47
|
<div className="mt-4">
|
|
48
|
-
<Label htmlFor="password_confirmation">Confirm
|
|
48
|
+
<Label htmlFor="password_confirmation">Confirm password</Label>
|
|
49
49
|
|
|
50
50
|
<Input
|
|
51
51
|
type="password"
|
|
@@ -61,7 +61,7 @@ export default function ResetPassword({ token }: { token: string }) {
|
|
|
61
61
|
|
|
62
62
|
<div className="flex items-center justify-end mt-4">
|
|
63
63
|
<Button className="ms-4" disabled={processing}>
|
|
64
|
-
Reset
|
|
64
|
+
Reset password
|
|
65
65
|
</Button>
|
|
66
66
|
</div>
|
|
67
67
|
</form>
|
|
@@ -15,7 +15,7 @@ export default function VerifyEmail({ status }: { status?: string }) {
|
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
17
|
<GuestLayout>
|
|
18
|
-
<Head title="Email
|
|
18
|
+
<Head title="Email verification" />
|
|
19
19
|
|
|
20
20
|
<div className="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
|
21
21
|
Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we
|
|
@@ -30,7 +30,7 @@ export default function VerifyEmail({ status }: { status?: string }) {
|
|
|
30
30
|
|
|
31
31
|
<form onSubmit={submit}>
|
|
32
32
|
<div className="mt-4 flex items-center justify-between">
|
|
33
|
-
<Button disabled={processing}>Resend
|
|
33
|
+
<Button disabled={processing}>Resend verification email</Button>
|
|
34
34
|
|
|
35
35
|
<Link
|
|
36
36
|
href={logout_path()}
|
|
@@ -38,7 +38,7 @@ export default function VerifyEmail({ status }: { status?: string }) {
|
|
|
38
38
|
as="button"
|
|
39
39
|
className="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800"
|
|
40
40
|
>
|
|
41
|
-
Log
|
|
41
|
+
Log out
|
|
42
42
|
</Link>
|
|
43
43
|
</div>
|
|
44
44
|
</form>
|
|
@@ -55,7 +55,7 @@ export default function DeleteUserForm({ className = '' }: { className?: string
|
|
|
55
55
|
return (
|
|
56
56
|
<section className={`space-y-6 ${className}`}>
|
|
57
57
|
<Button variant="destructive" onClick={confirmUserDeletion}>
|
|
58
|
-
Delete
|
|
58
|
+
Delete account
|
|
59
59
|
</Button>
|
|
60
60
|
|
|
61
61
|
<Dialog open={confirmingUserDeletion} onOpenChange={(open) => !open && closeModal()}>
|
|
@@ -96,7 +96,7 @@ export default function DeleteUserForm({ className = '' }: { className?: string
|
|
|
96
96
|
</Button>
|
|
97
97
|
|
|
98
98
|
<Button variant="destructive" disabled={processing}>
|
|
99
|
-
Delete
|
|
99
|
+
Delete account
|
|
100
100
|
</Button>
|
|
101
101
|
</DialogFooter>
|
|
102
102
|
</form>
|
|
@@ -41,7 +41,7 @@ export default function UpdatePasswordForm({ className = '' }: { className?: str
|
|
|
41
41
|
<section className={className}>
|
|
42
42
|
<form onSubmit={updatePassword} className="space-y-6">
|
|
43
43
|
<div>
|
|
44
|
-
<Label htmlFor="current_password">Current
|
|
44
|
+
<Label htmlFor="current_password">Current password</Label>
|
|
45
45
|
|
|
46
46
|
<Input
|
|
47
47
|
id="current_password"
|
|
@@ -57,7 +57,7 @@ export default function UpdatePasswordForm({ className = '' }: { className?: str
|
|
|
57
57
|
</div>
|
|
58
58
|
|
|
59
59
|
<div>
|
|
60
|
-
<Label htmlFor="password">New
|
|
60
|
+
<Label htmlFor="password">New password</Label>
|
|
61
61
|
|
|
62
62
|
<Input
|
|
63
63
|
id="password"
|
|
@@ -73,7 +73,7 @@ export default function UpdatePasswordForm({ className = '' }: { className?: str
|
|
|
73
73
|
</div>
|
|
74
74
|
|
|
75
75
|
<div>
|
|
76
|
-
<Label htmlFor="password_confirmation">Confirm
|
|
76
|
+
<Label htmlFor="password_confirmation">Confirm password</Label>
|
|
77
77
|
|
|
78
78
|
<Input
|
|
79
79
|
id="password_confirmation"
|
|
@@ -22,7 +22,7 @@ const submit = () => {
|
|
|
22
22
|
|
|
23
23
|
<template>
|
|
24
24
|
<GuestLayout>
|
|
25
|
-
<Head title="Forgot
|
|
25
|
+
<Head title="Forgot password" />
|
|
26
26
|
|
|
27
27
|
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
|
28
28
|
Forgot your password? No problem. Just let us know your email address and we will email you a password reset link
|
|
@@ -52,7 +52,7 @@ const submit = () => {
|
|
|
52
52
|
|
|
53
53
|
<div class="flex items-center justify-end mt-4">
|
|
54
54
|
<Button :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
|
55
|
-
Email
|
|
55
|
+
Email password reset link
|
|
56
56
|
</Button>
|
|
57
57
|
</div>
|
|
58
58
|
</form>
|
|
@@ -28,7 +28,7 @@ const submit = () => {
|
|
|
28
28
|
|
|
29
29
|
<template>
|
|
30
30
|
<GuestLayout>
|
|
31
|
-
<Head title="Reset
|
|
31
|
+
<Head title="Reset password" />
|
|
32
32
|
|
|
33
33
|
<form @submit.prevent="submit">
|
|
34
34
|
<div>
|
|
@@ -48,7 +48,7 @@ const submit = () => {
|
|
|
48
48
|
</div>
|
|
49
49
|
|
|
50
50
|
<div class="mt-4">
|
|
51
|
-
<Label for="password_confirmation">Confirm
|
|
51
|
+
<Label for="password_confirmation">Confirm password</Label>
|
|
52
52
|
|
|
53
53
|
<Input
|
|
54
54
|
id="password_confirmation"
|
|
@@ -63,7 +63,7 @@ const submit = () => {
|
|
|
63
63
|
</div>
|
|
64
64
|
|
|
65
65
|
<div class="flex items-center justify-end mt-4">
|
|
66
|
-
<Button :class="{ 'opacity-25': form.processing }" :disabled="form.processing"> Reset
|
|
66
|
+
<Button :class="{ 'opacity-25': form.processing }" :disabled="form.processing"> Reset password </Button>
|
|
67
67
|
</div>
|
|
68
68
|
</form>
|
|
69
69
|
</GuestLayout>
|
|
@@ -20,7 +20,7 @@ const verificationLinkSent = computed(() => props.status === 'verification-link-
|
|
|
20
20
|
|
|
21
21
|
<template>
|
|
22
22
|
<GuestLayout>
|
|
23
|
-
<Head title="Email
|
|
23
|
+
<Head title="Email verification" />
|
|
24
24
|
|
|
25
25
|
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
|
26
26
|
Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just
|
|
@@ -34,7 +34,7 @@ const verificationLinkSent = computed(() => props.status === 'verification-link-
|
|
|
34
34
|
<form @submit.prevent="submit">
|
|
35
35
|
<div class="mt-4 flex items-center justify-between">
|
|
36
36
|
<Button :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
|
37
|
-
Resend
|
|
37
|
+
Resend verification email
|
|
38
38
|
</Button>
|
|
39
39
|
|
|
40
40
|
<Link
|
|
@@ -42,7 +42,7 @@ const verificationLinkSent = computed(() => props.status === 'verification-link-
|
|
|
42
42
|
method="post"
|
|
43
43
|
as="button"
|
|
44
44
|
class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800"
|
|
45
|
-
>Log
|
|
45
|
+
>Log out</Link
|
|
46
46
|
>
|
|
47
47
|
</div>
|
|
48
48
|
</form>
|
|
@@ -49,7 +49,7 @@ const closeModal = () => {
|
|
|
49
49
|
|
|
50
50
|
<template>
|
|
51
51
|
<section class="space-y-6">
|
|
52
|
-
<Button variant="destructive" @click="confirmUserDeletion">Delete
|
|
52
|
+
<Button variant="destructive" @click="confirmUserDeletion">Delete account</Button>
|
|
53
53
|
|
|
54
54
|
<Dialog v-model:open="confirmingUserDeletion">
|
|
55
55
|
<DialogContent>
|
|
@@ -87,7 +87,7 @@ const closeModal = () => {
|
|
|
87
87
|
:disabled="form.processing"
|
|
88
88
|
@click="deleteUser"
|
|
89
89
|
>
|
|
90
|
-
Delete
|
|
90
|
+
Delete account
|
|
91
91
|
</Button>
|
|
92
92
|
</DialogFooter>
|
|
93
93
|
</DialogContent>
|
|
@@ -40,7 +40,7 @@ const updatePassword = () => {
|
|
|
40
40
|
<section>
|
|
41
41
|
<form @submit.prevent="updatePassword" class="space-y-6">
|
|
42
42
|
<div>
|
|
43
|
-
<Label for="current_password">Current
|
|
43
|
+
<Label for="current_password">Current password</Label>
|
|
44
44
|
|
|
45
45
|
<Input
|
|
46
46
|
id="current_password"
|
|
@@ -55,7 +55,7 @@ const updatePassword = () => {
|
|
|
55
55
|
</div>
|
|
56
56
|
|
|
57
57
|
<div>
|
|
58
|
-
<Label for="password">New
|
|
58
|
+
<Label for="password">New password</Label>
|
|
59
59
|
|
|
60
60
|
<Input
|
|
61
61
|
id="password"
|
|
@@ -70,7 +70,7 @@ const updatePassword = () => {
|
|
|
70
70
|
</div>
|
|
71
71
|
|
|
72
72
|
<div>
|
|
73
|
-
<Label for="password_confirmation">Confirm
|
|
73
|
+
<Label for="password_confirmation">Confirm password</Label>
|
|
74
74
|
|
|
75
75
|
<Input
|
|
76
76
|
id="password_confirmation"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kaze
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cuong Giang
|
|
@@ -37,6 +37,20 @@ dependencies:
|
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '2.6'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: minitest
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '5.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '5.0'
|
|
40
54
|
- !ruby/object:Gem::Dependency
|
|
41
55
|
name: railties
|
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|