nitro_kit 2.0.0.alpha.3 → 2.0.0.alpha.4
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 +179 -0
- data/README.md +22 -26
- data/STYLE_GUIDE.md +10 -0
- data/app/assets/stylesheets/nitro_kit-tailwind-v4.css +3 -3
- data/app/assets/stylesheets/nitro_kit.css +1220 -572
- data/app/components/nitro_kit/accordion.rb +1 -19
- data/app/components/nitro_kit/alert.rb +1 -11
- data/app/components/nitro_kit/app_navigation.rb +44 -9
- data/app/components/nitro_kit/appearance_picker.rb +10 -1
- data/app/components/nitro_kit/badge.rb +13 -4
- data/app/components/nitro_kit/combobox.rb +1 -17
- data/app/components/nitro_kit/command_palette.rb +1 -0
- data/app/components/nitro_kit/dropzone.rb +1 -1
- data/app/components/nitro_kit/form_builder.rb +1 -1
- data/app/components/nitro_kit/select.rb +1 -14
- data/app/components/nitro_kit/tabs.rb +1 -0
- data/app/javascript/controllers/nk/command_palette_controller.js +4 -0
- data/app/javascript/controllers/nk/tabs_controller.js +13 -1
- data/docs/agent_guide.md +63 -138
- data/docs/agent_native_spec.md +72 -400
- data/docs/browser_support.md +77 -188
- data/docs/component_contracts.md +56 -36
- data/docs/customization.md +675 -89
- data/docs/hotwire.md +61 -99
- data/docs/initialization_prompt.md +27 -57
- data/docs/migration_1_to_2.md +34 -18
- data/docs/patterns/application_foundation.md +45 -121
- data/docs/patterns/crud_resource.md +44 -129
- data/docs/patterns/destructive_action.md +45 -102
- data/docs/patterns/flash_and_toast.md +18 -36
- data/docs/patterns/inline_edit.md +21 -44
- data/docs/patterns/queryable_collection.md +65 -161
- data/docs/patterns/resource_form.md +33 -81
- data/docs/rails_conventions.md +38 -78
- data/docs/rails_integration.md +96 -474
- data/lib/nitro_kit/installation.rb +11 -3
- data/lib/nitro_kit/migration_inventory.rb +15 -8
- data/lib/nitro_kit/version.rb +1 -1
- data/plugins/nitro-kit/skills/nitro-kit-rails/SKILL.md +7 -0
- data/plugins/nitro-kit/skills/nitro-kit-ui/SKILL.md +15 -8
- data/src/stylesheets/nitro_kit/components/accordion.css +4 -4
- data/src/stylesheets/nitro_kit/components/alert.css +20 -29
- data/src/stylesheets/nitro_kit/components/app_navigation.css +74 -2
- data/src/stylesheets/nitro_kit/components/app_shell.css +3 -3
- data/src/stylesheets/nitro_kit/components/appearance_picker.css +20 -8
- data/src/stylesheets/nitro_kit/components/auth_shell.css +2 -2
- data/src/stylesheets/nitro_kit/components/avatar.css +12 -8
- data/src/stylesheets/nitro_kit/components/avatar_stack.css +12 -10
- data/src/stylesheets/nitro_kit/components/badge.css +13 -9
- data/src/stylesheets/nitro_kit/components/button.css +42 -25
- data/src/stylesheets/nitro_kit/components/card.css +2 -2
- data/src/stylesheets/nitro_kit/components/checkbox.css +29 -46
- data/src/stylesheets/nitro_kit/components/checkbox_group.css +3 -3
- data/src/stylesheets/nitro_kit/components/combobox.css +6 -6
- data/src/stylesheets/nitro_kit/components/command_palette.css +62 -4
- data/src/stylesheets/nitro_kit/components/container.css +6 -6
- data/src/stylesheets/nitro_kit/components/control_group.css +9 -9
- data/src/stylesheets/nitro_kit/components/danger_zone.css +6 -6
- data/src/stylesheets/nitro_kit/components/data_section.css +4 -4
- data/src/stylesheets/nitro_kit/components/details_table.css +7 -5
- data/src/stylesheets/nitro_kit/components/dialog.css +3 -3
- data/src/stylesheets/nitro_kit/components/dropdown.css +7 -7
- data/src/stylesheets/nitro_kit/components/dropzone.css +7 -7
- data/src/stylesheets/nitro_kit/components/empty_state.css +4 -4
- data/src/stylesheets/nitro_kit/components/field.css +2 -2
- data/src/stylesheets/nitro_kit/components/fieldset.css +2 -2
- data/src/stylesheets/nitro_kit/components/flex.css +1 -1
- data/src/stylesheets/nitro_kit/components/grid.css +1 -1
- data/src/stylesheets/nitro_kit/components/icon.css +5 -5
- data/src/stylesheets/nitro_kit/components/input.css +33 -6
- data/src/stylesheets/nitro_kit/components/page_header.css +4 -4
- data/src/stylesheets/nitro_kit/components/pagination.css +3 -3
- data/src/stylesheets/nitro_kit/components/pagination_bar.css +2 -2
- data/src/stylesheets/nitro_kit/components/palette.css +140 -72
- data/src/stylesheets/nitro_kit/components/progressive_image.css +1 -1
- data/src/stylesheets/nitro_kit/components/radio_button.css +28 -33
- data/src/stylesheets/nitro_kit/components/radio_button_group.css +6 -6
- data/src/stylesheets/nitro_kit/components/rich_text_area.css +2 -2
- data/src/stylesheets/nitro_kit/components/select.css +24 -8
- data/src/stylesheets/nitro_kit/components/settings_layout.css +5 -5
- data/src/stylesheets/nitro_kit/components/settings_section.css +2 -2
- data/src/stylesheets/nitro_kit/components/sheet.css +10 -10
- data/src/stylesheets/nitro_kit/components/stat_grid.css +1 -1
- data/src/stylesheets/nitro_kit/components/switch.css +34 -40
- data/src/stylesheets/nitro_kit/components/table.css +8 -4
- data/src/stylesheets/nitro_kit/components/tabs.css +5 -1
- data/src/stylesheets/nitro_kit/components/textarea.css +20 -5
- data/src/stylesheets/nitro_kit/components/toast.css +11 -58
- data/src/stylesheets/nitro_kit/components/toolbar.css +2 -2
- data/src/stylesheets/nitro_kit/components/tooltip.css +4 -0
- data/src/stylesheets/nitro_kit/components/typeset.css +4 -4
- data/src/stylesheets/nitro_kit/reset.css +6 -6
- data/src/stylesheets/nitro_kit/tokens.css +497 -74
- metadata +1 -2
- data/docs/new_app_strategy.md +0 -22
data/docs/hotwire.md
CHANGED
|
@@ -1,106 +1,68 @@
|
|
|
1
1
|
# Hotwire with Nitro Kit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
**Audience:** Application developers and coding agents implementing Hotwire
|
|
4
|
+
interactions with Nitro Kit.
|
|
5
|
+
|
|
6
|
+
Rails owns records, routes, authorization, DOM identity, and responses. Nitro
|
|
7
|
+
Kit owns component markup, CSS, and its focused controllers. Hotwire transports
|
|
8
|
+
server-rendered HTML between them.
|
|
6
9
|
|
|
7
10
|
## Choose the smallest interaction
|
|
8
11
|
|
|
9
|
-
1.
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Use `dom_id` or one named constant for
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
import { Controller } from "@hotwired/stimulus";
|
|
58
|
-
|
|
59
|
-
export default class extends Controller {
|
|
60
|
-
submit() {
|
|
61
|
-
this.element.requestSubmit();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Attach `data-action="change->auto-submit#submit"` to the form so change events
|
|
67
|
-
bubble to that one controller root. Keep a submit button inside `noscript` as
|
|
68
|
-
the HTML fallback. Use `data-action` instead of manually registering DOM listeners. If a
|
|
69
|
-
controller owns a listener, observer, timer, object URL, or third-party
|
|
70
|
-
instance, release it in `disconnect`.
|
|
71
|
-
|
|
72
|
-
## Morphing and cache lifecycle
|
|
73
|
-
|
|
74
|
-
Default to morphable server-rendered HTML. Use `data-turbo-permanent` only for
|
|
75
|
-
a genuinely stateful island, always with a stable unique `id`. Update content
|
|
76
|
-
inside a permanent element deliberately instead of making broad page regions
|
|
77
|
-
permanent.
|
|
78
|
-
|
|
79
|
-
Clean ephemeral UI before Turbo caches the page. Nitro Kit controllers own
|
|
80
|
-
their own cache and reconnect behavior; application controllers must do the
|
|
81
|
-
same for application-owned state. `nk--dialog` closes an open Dialog, Sheet, or
|
|
82
|
-
CommandPalette on `turbo:before-cache`; reconnecting does not install listeners
|
|
83
|
-
or retain a mirrored open flag.
|
|
84
|
-
|
|
85
|
-
## Frame escape and recovery
|
|
86
|
-
|
|
87
|
-
An authentication redirect or error response inside a frame must not strand
|
|
88
|
-
the user behind a missing-frame error. Use `_top` when navigation must leave
|
|
89
|
-
the frame. Keep authentication and authorization handling capable of returning
|
|
90
|
-
a full-page response, and handle `turbo:frame-missing` only when the
|
|
91
|
-
application has a deliberate recovery policy.
|
|
92
|
-
|
|
93
|
-
Give lazy frames meaningful loading content. A failed frame should leave an
|
|
94
|
-
understandable state and a path to retry or continue without JavaScript.
|
|
95
|
-
|
|
96
|
-
## Verify behavior
|
|
12
|
+
1. Ordinary links and forms under Turbo Drive.
|
|
13
|
+
2. One Turbo Frame for one independently navigable region.
|
|
14
|
+
3. A request-scoped Turbo Stream when one action changes multiple regions.
|
|
15
|
+
4. A broadcast only when another session needs the update.
|
|
16
|
+
5. Application Stimulus only for browser-owned behavior the previous layers
|
|
17
|
+
cannot express.
|
|
18
|
+
|
|
19
|
+
Do not copy Nitro controllers or add files under
|
|
20
|
+
`app/javascript/controllers/nk`. Follow the canonical
|
|
21
|
+
[browser support policy](browser_support.md) for fallback behavior.
|
|
22
|
+
|
|
23
|
+
## Response contract
|
|
24
|
+
|
|
25
|
+
| Request | Success | Validation failure |
|
|
26
|
+
| --------------- | ----------------------------------------- | --------------------------------------------------------------- |
|
|
27
|
+
| HTML mutation | Redirect with `303 See Other` | Render the invalid form with `422` |
|
|
28
|
+
| Frame mutation | Redirect to, or render, the same frame ID | Render the same frame ID with `422` |
|
|
29
|
+
| Stream mutation | Return a stream only for multiple targets | Replace the invalid form target with `422`; keep an HTML branch |
|
|
30
|
+
| GET query | Render from URL parameters | Render a useful empty or error state |
|
|
31
|
+
|
|
32
|
+
Authentication and authorization failures are separate policy decisions; do
|
|
33
|
+
not return `422` for them.
|
|
34
|
+
|
|
35
|
+
Use `dom_id` or one named constant for each frame. Show, edit, invalid, success,
|
|
36
|
+
and cancel responses must preserve that identifier. An HTML branch preserves
|
|
37
|
+
the request path without Turbo; it does not make a closed or JavaScript-owned
|
|
38
|
+
interaction available.
|
|
39
|
+
|
|
40
|
+
## Stimulus and lifecycle
|
|
41
|
+
|
|
42
|
+
Let Turbo submit real Rails forms. Use `data-turbo-submits-with` for submission
|
|
43
|
+
feedback and `data-turbo-confirm` only for compact confirmation. A reviewed
|
|
44
|
+
destructive flow still submits a real Rails form; use the
|
|
45
|
+
[destructive action pattern](patterns/destructive_action.md).
|
|
46
|
+
|
|
47
|
+
Keep application controllers declarative. Prefer `data-action` over manually
|
|
48
|
+
registered listeners. Release listeners, observers, timers, object URLs, and
|
|
49
|
+
third-party instances in `disconnect`.
|
|
50
|
+
|
|
51
|
+
Keep server-rendered markup morph-safe. Enable refresh morphing deliberately,
|
|
52
|
+
and use `data-turbo-permanent` only for a stateful island with a stable unique
|
|
53
|
+
ID. Clean ephemeral UI before Turbo caches a page.
|
|
54
|
+
|
|
55
|
+
Use `_top` when navigation must leave a frame. Authentication redirects and
|
|
56
|
+
errors must not strand the user behind a missing-frame response. Give lazy and
|
|
57
|
+
failed frames useful content and a route to continue.
|
|
58
|
+
|
|
59
|
+
## Verify
|
|
97
60
|
|
|
98
61
|
- Request-test `303`, `422`, HTML fallback, and stable frame IDs.
|
|
99
|
-
-
|
|
100
|
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Read the matching recipe under `docs/patterns/` for complete compositions.
|
|
62
|
+
- Scope mutation assertions to the matching frame.
|
|
63
|
+
- System-test focus, navigation, dialogs, and multi-target changes.
|
|
64
|
+
- Use Capybara waiting assertions; never use `sleep`.
|
|
65
|
+
- Test navigation, caching, morphing, and reconnection for duplicate state or
|
|
66
|
+
listeners.
|
|
67
|
+
|
|
68
|
+
Use the matching [interaction pattern](patterns/) for complete compositions.
|
|
@@ -1,61 +1,31 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Verify Nitro Kit 2 setup in this Rails application
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
APIs or examples.
|
|
3
|
+
**Audience:** Coding agent running immediately after Nitro Kit installation.
|
|
5
4
|
|
|
6
|
-
1. Run `bundle show nitro_kit` and confirm the resolved version
|
|
5
|
+
1. Run `bundle show nitro_kit` and confirm the resolved version starts with
|
|
7
6
|
`2.`.
|
|
8
|
-
2.
|
|
9
|
-
|
|
10
|
-
3. Inspect the application before editing.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
discover the gem-owned `nk--*` controllers. Never copy Nitro components,
|
|
25
|
-
helpers, or controllers into the application.
|
|
26
|
-
7. Remove confirmed Nitro Kit 1.x shadows such as application-owned
|
|
27
|
-
`NitroKit` components, `nk_*` helpers, or `controllers/nk` only when this
|
|
28
|
-
task is authorized to migrate the application.
|
|
29
|
-
8. Use ordinary Rails routes, models, forms, and server-rendered HTML. Follow
|
|
30
|
-
the installed Rails and Hotwire guidance for new work. During a migration,
|
|
31
|
-
replace an existing control only when Nitro Kit 2 has a genuine semantic
|
|
32
|
-
and behavioral equivalent. Preserve compound ownership: use `ButtonGroup`
|
|
33
|
-
for joined actions and `ControlGroup` for joined inputs, addons, and buttons
|
|
34
|
-
instead of rebuilding their geometry with a raw flex wrapper. Otherwise
|
|
35
|
-
preserve the control as application-owned Rails and semantic HTML; never
|
|
36
|
-
downgrade specialized behavior or retain copied Nitro Kit 1.x source as the
|
|
37
|
-
fallback. Preserve strict component boundaries: route native attributes
|
|
38
|
-
through `html:`, `aria:`, or `data:`, explicitly name icon-only Buttons and
|
|
39
|
-
triggers, and give custom `form.field` blocks explicit labels.
|
|
40
|
-
9. Translate the application's existing semantic theme into documented public
|
|
41
|
-
`--nk-*` tokens. Preserve primary, focus, danger, neutral, font, density, and
|
|
42
|
-
radius decisions rather than selecting visually similar raw palette values.
|
|
43
|
-
Use `--nk-button-radius` when buttons intentionally have a different shape
|
|
44
|
-
from inputs and surfaces.
|
|
45
|
-
10. Run `bin/rails nitro_kit:doctor`, fix actionable failures, and run the
|
|
46
|
-
application's relevant tests plus the generated upgrade smoke tests. When
|
|
47
|
-
the application uses strict i18n, render representative forms with
|
|
48
|
-
`ActiveModel::Translation.raise_on_missing_translations` enabled. Doctor
|
|
49
|
-
inventories migration work; a clean result is not runtime or visual
|
|
50
|
-
verification.
|
|
51
|
-
11. Compare the same representative flows across the dated matrix in
|
|
52
|
-
`docs/browser_support.md`, including Mobile Safari where available, at wide
|
|
53
|
-
and narrow widths. Exercise keyboard focus and inspect computed styles for missing
|
|
54
|
-
application classes, stacked Button content, broken compound corners,
|
|
55
|
-
double focus rings, clipping, and theme drift. Re-audit rendered native
|
|
56
|
-
buttons, `button_tag`, `submit_tag`, and application-owned button classes
|
|
57
|
-
before declaring the migration complete.
|
|
7
|
+
2. Choose the project-local Nitro Kit skill matching the task. It will resolve
|
|
8
|
+
and read the installed, version-matched `docs/agent_guide.md`.
|
|
9
|
+
3. Inspect the application before editing. Preserve established view, asset,
|
|
10
|
+
authentication, and testing conventions unless the task changes them.
|
|
11
|
+
4. For a greenfield application, run `bin/rails generate phlex:install` and use
|
|
12
|
+
Phlex for the application layout, route views, and reusable UI. In an
|
|
13
|
+
established application, introduce Phlex only at the requested boundary.
|
|
14
|
+
Do not perform an application-wide migration unless it is explicitly
|
|
15
|
+
authorized.
|
|
16
|
+
5. Verify that the application loads Nitro Kit CSS, the appearance bootstrap,
|
|
17
|
+
Turbo, Stimulus, and the normal Stimulus controller loader. Never copy Nitro
|
|
18
|
+
components or `nk--*` controllers into the application.
|
|
19
|
+
6. Verify one application base component includes `NitroKit`, and model-backed
|
|
20
|
+
forms select `NitroKit::FormBuilder` explicitly.
|
|
21
|
+
7. Run `bin/rails nitro_kit:doctor`, fix actionable failures, and run the
|
|
22
|
+
application's relevant tests.
|
|
58
23
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
24
|
+
If this is a Nitro Kit 1.x migration, stop and follow
|
|
25
|
+
`docs/migration_1_to_2.md` from the installed gem. Replace a control only when
|
|
26
|
+
2.x provides a genuine semantic and behavioral equivalent. Otherwise preserve
|
|
27
|
+
it as application-owned Rails and semantic HTML. Never retain copied Nitro Kit
|
|
28
|
+
1.x source as the fallback.
|
|
29
|
+
|
|
30
|
+
Report changes, preserved conventions, unsupported controls, and unresolved
|
|
31
|
+
decisions.
|
data/docs/migration_1_to_2.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Migrating a Nitro Kit 1.x application
|
|
2
2
|
|
|
3
|
+
**Audience:** Coding agents and developers migrating an existing application
|
|
4
|
+
from Nitro Kit 1.x to 2.x.
|
|
5
|
+
|
|
3
6
|
Treat a 1.x migration as a product-flow review, not a helper rename. Nitro Kit
|
|
4
7
|
2 deliberately removed copied components, `nk_*` helpers, application-owned
|
|
5
8
|
`controllers/nk`, and unrestricted utility-class customization.
|
|
@@ -9,7 +12,7 @@ Treat a 1.x migration as a product-flow review, not a helper rename. Nitro Kit
|
|
|
9
12
|
Add the 2.0 prerelease to the application's Gemfile:
|
|
10
13
|
|
|
11
14
|
```ruby
|
|
12
|
-
gem "nitro_kit", "2.0.0.alpha.
|
|
15
|
+
gem "nitro_kit", "2.0.0.alpha.4"
|
|
13
16
|
```
|
|
14
17
|
|
|
15
18
|
Bundler records the exact released version in `Gemfile.lock`; commit `Gemfile`
|
|
@@ -30,7 +33,7 @@ rather than a moving Git branch.
|
|
|
30
33
|
3. Record behavior that must survive: native element, submitted method and
|
|
31
34
|
parameters, Turbo target, accessible name and description, focus behavior,
|
|
32
35
|
narrow-screen presentation, empty/error state, and visual density.
|
|
33
|
-
4. Inventory the existing semantic primary, focus,
|
|
36
|
+
4. Inventory the existing semantic primary, focus, destructive, neutral, font,
|
|
34
37
|
density, and radius decisions. Translate those roles to public `--nk-*`
|
|
35
38
|
tokens rather than choosing similar raw palette values. Record separately
|
|
36
39
|
when buttons use a distinct shape from inputs and surfaces.
|
|
@@ -113,7 +116,7 @@ end
|
|
|
113
116
|
```
|
|
114
117
|
|
|
115
118
|
The executable minimal version is on the
|
|
116
|
-
[`AppNavigation` gallery page](/gallery/components/app-navigation#example-app-navigation-minimal).
|
|
119
|
+
[`AppNavigation` gallery page](https://gallery.nitrokit.dev/gallery/components/app-navigation#example-app-navigation-minimal).
|
|
117
120
|
|
|
118
121
|
### Dialog
|
|
119
122
|
|
|
@@ -164,7 +167,7 @@ end
|
|
|
164
167
|
```
|
|
165
168
|
|
|
166
169
|
The gallery runs this structure at narrow widths in
|
|
167
|
-
[`Narrow transcript actions`](/gallery/components/dialog#example-dialog-narrow-action-cluster).
|
|
170
|
+
[`Narrow transcript actions`](https://gallery.nitrokit.dev/gallery/components/dialog#example-dialog-narrow-action-cluster).
|
|
168
171
|
|
|
169
172
|
### Sheet
|
|
170
173
|
|
|
@@ -204,7 +207,7 @@ end
|
|
|
204
207
|
```
|
|
205
208
|
|
|
206
209
|
See the executable
|
|
207
|
-
[`Sheet` collection example](/gallery/components/sheet#example-sheet-constructions).
|
|
210
|
+
[`Sheet` collection example](https://gallery.nitrokit.dev/gallery/components/sheet#example-sheet-constructions).
|
|
208
211
|
|
|
209
212
|
### SettingsLayout
|
|
210
213
|
|
|
@@ -243,13 +246,22 @@ end
|
|
|
243
246
|
```
|
|
244
247
|
|
|
245
248
|
See the executable
|
|
246
|
-
[`SettingsLayout` minimal example](/gallery/components/settings-layout#example-settings-layout-cardinality-states).
|
|
249
|
+
[`SettingsLayout` minimal example](https://gallery.nitrokit.dev/gallery/components/settings-layout#example-settings-layout-cardinality-states).
|
|
247
250
|
|
|
248
251
|
## Preserve unsupported behavior honestly
|
|
249
252
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
+
Replace a form control only when the installed Nitro Kit catalog provides a
|
|
254
|
+
genuine semantic and behavioral equivalent. Preserve its parameter name, IDs,
|
|
255
|
+
values, errors, accessibility, uploads, and browser behavior. If no equivalent
|
|
256
|
+
exists, keep or re-express the control as application-owned Rails and semantic
|
|
257
|
+
HTML, optionally inside a custom `form.field` composition. Never downgrade an
|
|
258
|
+
editor, autocomplete, date range, upload, or other specialized input to the
|
|
259
|
+
nearest generic Nitro control merely for visual consistency.
|
|
260
|
+
|
|
261
|
+
Do not retain copied Nitro Kit 1.x source as the fallback. Remove the legacy
|
|
262
|
+
component, preserve the unsupported behavior in clearly application-owned
|
|
263
|
+
code, and report the missing equivalent as a Nitro Kit coverage gap. Do not
|
|
264
|
+
hide the gap behind a generic component.
|
|
253
265
|
|
|
254
266
|
Tooltip custom triggers are the explicit composition path for an existing
|
|
255
267
|
focusable control. Forward every yielded boundary to that actual control:
|
|
@@ -282,15 +294,19 @@ bin/rails test test/system/nitro_kit_upgrade_smoke_test.rb
|
|
|
282
294
|
|
|
283
295
|
The generator does not overwrite existing tests. It generates only files
|
|
284
296
|
supported by the host's Rails Minitest and system-test setup and prints setup
|
|
285
|
-
guidance for skipped files.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
and
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
297
|
+
guidance for skipped files. Keep the generated explanatory header in each
|
|
298
|
+
test: these are host-integration regression checks, not substitutes for
|
|
299
|
+
application-specific acceptance tests. Run every supported generated test
|
|
300
|
+
before and after the application conversion, while retaining
|
|
301
|
+
application-specific acceptance coverage for every inventoried flow.
|
|
302
|
+
|
|
303
|
+
The tests use the currently bundled gem and cover the shared upgrade boundary
|
|
304
|
+
— browser-submitted Turbo validation and mutation, Dialog and Sheet,
|
|
305
|
+
layout-owned Rails flash, Turbo Frame identity, redirects, and post-mutation
|
|
306
|
+
Phlex rendering. Their route is prepended only during each test, so host
|
|
307
|
+
catch-all routes remain compatible; an exact GET or PATCH route at the same
|
|
308
|
+
path is still rejected rather than masked. The route is restored afterward and
|
|
309
|
+
adds no production route or component source.
|
|
294
310
|
|
|
295
311
|
The endpoint deliberately inherits `ApplicationController` callbacks. If the
|
|
296
312
|
application requires authentication or current-account state, fill in the
|
|
@@ -1,138 +1,62 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Application foundation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
smallest instance of the team model, not a separate personal mode that must be
|
|
6
|
-
replaced later.
|
|
3
|
+
**Audience:** Coding agents and developers starting an authenticated,
|
|
4
|
+
team-aware Nitro Kit application.
|
|
7
5
|
|
|
8
6
|
## Summary
|
|
9
7
|
|
|
10
|
-
- Model `User`, `Team`, and `Membership
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
compose with `SettingsLayout` and plain `SettingsSection` regions.
|
|
19
|
-
- When destination count warrants search, compose one `CommandPalette` in the
|
|
20
|
-
shell and render only routes the current membership may visit.
|
|
21
|
-
- Centralize cross-cutting feedback in one flash-driven toast region rendered
|
|
22
|
-
by the layout, and set the document language on the root `html` element.
|
|
8
|
+
- Model `User`, `Team`, and `Membership`; roles belong to memberships, and
|
|
9
|
+
tenant-owned records load through `Current.team`.
|
|
10
|
+
- Use one `AppShell` for the authenticated product and one application-owned
|
|
11
|
+
content gutter inside `shell.main`.
|
|
12
|
+
- Put route titles and persistent actions in the shell `Toolbar`; keep
|
|
13
|
+
destinations in `AppNavigation`.
|
|
14
|
+
- Use links for settings destinations and one layout-level
|
|
15
|
+
`Toast::FlashMessages` region for server feedback.
|
|
23
16
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
Use `User`, `Team`, and `Membership` even when signup creates exactly one of
|
|
27
|
-
each. Put the role on `Membership`, not `User`, because authority belongs to a
|
|
28
|
-
person's relationship with a team. Create the first team and owner membership
|
|
29
|
-
in the same transaction as signup.
|
|
17
|
+
## Membership and current context
|
|
30
18
|
|
|
19
|
+
Create the first team and owner membership in the same transaction as signup.
|
|
31
20
|
Set `Current.user`, `Current.membership`, and `Current.team` from the session.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
product needs, usually owner, administrator, and member. Protect the last owner
|
|
35
|
-
in the domain model rather than only hiding a button.
|
|
36
|
-
|
|
37
|
-
Invitations should belong to a team, record the inviter and intended role,
|
|
38
|
-
expire, match the invited email, and be consumed when accepted. An existing
|
|
39
|
-
user accepts into a new membership; a new user completes signup and then uses
|
|
40
|
-
the same acceptance path.
|
|
41
|
-
|
|
42
|
-
## Compose one authenticated frame
|
|
43
|
-
|
|
44
|
-
Use one hybrid `AppShell` for the authenticated product. Keep the brand and
|
|
45
|
-
primary destinations in `AppNavigation`; place route titles and persistent
|
|
46
|
-
basic actions in the shell `Toolbar`. One wrapper immediately inside
|
|
47
|
-
`shell.main` owns responsive page padding for every route. The topbar and
|
|
48
|
-
sidebar header should use the shell's shared height and border tokens rather
|
|
49
|
-
than independent padding guesses. At narrow widths, allow trailing actions to
|
|
50
|
-
stack below the Back affordance and title so neither the title nor persistent
|
|
51
|
-
actions are clipped.
|
|
52
|
-
|
|
53
|
-
Put infrequent account navigation after `navigation.spacer`, near the account
|
|
54
|
-
controls at the bottom of the sidebar:
|
|
55
|
-
|
|
56
|
-
```ruby
|
|
57
|
-
shell.navigation do
|
|
58
|
-
AppNavigation(label: "Workspace navigation") do |navigation|
|
|
59
|
-
navigation.body do
|
|
60
|
-
navigation.item("Inventory", href: assets_path, icon: :archive)
|
|
61
|
-
navigation.item("Team", href: team_path, icon: :users)
|
|
62
|
-
navigation.spacer
|
|
63
|
-
navigation.item("Settings", href: settings_profile_path, icon: :settings)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Application code owns destinations and current-route policy. Nitro owns shell
|
|
70
|
-
layout, mobile disclosure, focus management, and navigation semantics.
|
|
71
|
-
For larger products, place one `CommandPalette` in the shell. Its native links
|
|
72
|
-
remain the navigation authority while Command-K or Control-K adds fast
|
|
73
|
-
filtering. Render the same authorized destination set the user can reach in
|
|
74
|
-
ordinary navigation; do not use the palette to bypass route policy.
|
|
75
|
-
When the destination set is too large or dynamic to render eagerly, pass
|
|
76
|
-
`search_url:` and return `CommandPalette::Results` from that endpoint. Keep the
|
|
77
|
-
same authorization scope on the initial links and every remote query.
|
|
78
|
-
Let the shell own viewport height and scrolling; do not add `min-height: 100vh`
|
|
79
|
-
to its main region or page wrapper. Put brand and destination icons through the
|
|
80
|
-
navigation slots so they share the same left alignment.
|
|
81
|
-
|
|
82
|
-
Team is an administration surface, not merely a roster. Include pending
|
|
83
|
-
invitations and the complete invite, role-change, removal, and revoke paths,
|
|
84
|
-
with last-owner protection in the model and authorization on every mutation.
|
|
85
|
-
|
|
86
|
-
Authentication is a standalone form surface. Inside `AuthShell`, use Rails
|
|
87
|
-
`form_with` with `NitroKit::FormBuilder` and put the visible fields, submit
|
|
88
|
-
control, and related recovery link in one `form.group`. The group owns their
|
|
89
|
-
vertical rhythm; `AuthShell` owns only the page container and spacing between
|
|
90
|
-
its major regions.
|
|
21
|
+
Use `Current.user` for authorship and audit fields; scope tenant data through
|
|
22
|
+
the team. Protect the last owner in the model.
|
|
91
23
|
|
|
92
|
-
|
|
24
|
+
Invitations belong to a team, record inviter and role, expire, and match the
|
|
25
|
+
invited email. Existing and new users should share one acceptance path.
|
|
93
26
|
|
|
94
|
-
|
|
95
|
-
stable subsections such as Profile, Notifications, Appearance, and Password;
|
|
96
|
-
its content renders the selected form. Use `SettingsSection` for genuinely distinct
|
|
97
|
-
form regions and ordinary whitespace or dividers between them.
|
|
27
|
+
## Authenticated shell
|
|
98
28
|
|
|
99
|
-
|
|
100
|
-
`
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
still works without JavaScript.
|
|
29
|
+
Use one `AppShell`, normally `layout: :hybrid`, for authenticated routes.
|
|
30
|
+
`AppNavigation` owns brand and destinations; a `Toolbar` in `shell.topbar`
|
|
31
|
+
owns the route's single `h1` and persistent actions. One wrapper inside
|
|
32
|
+
`shell.main` owns responsive page padding. Do not add another viewport-height
|
|
33
|
+
or outer-padding rule in child pages.
|
|
105
34
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
widths without JavaScript.
|
|
35
|
+
Application code owns destinations, authorization, and current-route policy.
|
|
36
|
+
Nitro owns responsive disclosure and focus behavior. Put infrequent account
|
|
37
|
+
destinations after `navigation.spacer`. Add one `CommandPalette` only when the
|
|
38
|
+
destination count warrants search, and render only authorized routes.
|
|
111
39
|
|
|
112
|
-
|
|
40
|
+
Use `AuthShell` with Rails `form_with` and `NitroKit::FormBuilder` for
|
|
41
|
+
authentication. Put visible fields, submit, and recovery link in one
|
|
42
|
+
`form.group`.
|
|
113
43
|
|
|
114
|
-
|
|
115
|
-
ordinary Rails flash and `303 See Other` redirects from controllers.
|
|
44
|
+
## Settings and feedback
|
|
116
45
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
browser's top layer keeps that inline dialog clear of ancestor clipping and
|
|
122
|
-
stacking contexts. Put record deletion on the edit route rather than adding a
|
|
123
|
-
danger surface to every operational show page.
|
|
46
|
+
Render `SettingsLayout` inside the normal shell. Settings destinations are
|
|
47
|
+
links with `aria-current="page"`, not Buttons. Use `SettingsSection` only for
|
|
48
|
+
distinct form regions; do not wrap every subsection in a Card or repeat the
|
|
49
|
+
route title.
|
|
124
50
|
|
|
125
|
-
|
|
51
|
+
Render `NitroKit::Toast::FlashMessages` once in the application layout. Keep
|
|
52
|
+
ordinary Rails flash and `303 See Other` redirects. Use the dedicated
|
|
53
|
+
[destructive action](destructive_action.md) and
|
|
54
|
+
[flash](flash_and_toast.md) patterns for those flows.
|
|
126
55
|
|
|
127
|
-
|
|
56
|
+
## Acceptance checklist
|
|
128
57
|
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
- successful mutations redirect with `303` and invalid forms render with
|
|
135
|
-
`422`;
|
|
136
|
-
- confirmation has both cancel and confirm coverage;
|
|
137
|
-
- the shell, headings, tables, and forms remain usable without custom request
|
|
138
|
-
JavaScript.
|
|
58
|
+
- Signup or sign-in selects a membership and team.
|
|
59
|
+
- Cross-team records cannot be loaded.
|
|
60
|
+
- Owner, administrator, and member policy differs where intended.
|
|
61
|
+
- Populated, empty, invalid, narrow, settings, and destructive states work.
|
|
62
|
+
- Successful mutations redirect with `303`; invalid forms render with `422`.
|