@elyracode/stack-tall 0.3.0
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.
- package/README.md +18 -0
- package/extensions/index.ts +13 -0
- package/package.json +27 -0
- package/skills/tall-stack/SKILL.md +309 -0
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @elyracode/stack-tall
|
|
2
|
+
|
|
3
|
+
Elyra stack profile for **TALL** (Tailwind CSS, Alpine.js, Laravel, Livewire 4, Flux UI).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
elyra install npm:@elyracode/stack-tall
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What's included
|
|
12
|
+
|
|
13
|
+
- **Skills**: Deep TALL stack knowledge (Livewire 4 lifecycle, attributes, hydration rules, Alpine patterns, Flux UI components, file structure conventions, common gotchas)
|
|
14
|
+
- **Commands**: `/tall:info` -- show stack profile status
|
|
15
|
+
|
|
16
|
+
## Stack Detection
|
|
17
|
+
|
|
18
|
+
Elyra automatically detects TALL stack projects and suggests this package.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@elyracode/coding-agent";
|
|
2
|
+
|
|
3
|
+
export default function (elyra: ExtensionAPI) {
|
|
4
|
+
// The TALL skill is auto-loaded from the skills/ directory.
|
|
5
|
+
// This extension adds stack-aware commands.
|
|
6
|
+
|
|
7
|
+
elyra.registerCommand("tall:info", {
|
|
8
|
+
description: "Show detected TALL stack information",
|
|
9
|
+
handler: async (ctx) => {
|
|
10
|
+
ctx.ui.notify("info", "TALL stack profile loaded. Skills: tall-stack. Use /skill:tall-stack for full reference.");
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@elyracode/stack-tall",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Elyra stack profile for TALL (Tailwind, Alpine.js, Laravel, Livewire 4, Flux UI)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": ["elyra-package", "tall", "laravel", "livewire", "tailwind", "alpine", "flux-ui"],
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "Knut W. Horne",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/kwhorne/elyra.git",
|
|
12
|
+
"directory": "packages/stack-tall"
|
|
13
|
+
},
|
|
14
|
+
"elyra": {
|
|
15
|
+
"skills": ["./skills"],
|
|
16
|
+
"extensions": ["./extensions/index.ts"]
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@elyracode/coding-agent": "*",
|
|
20
|
+
"typebox": "*"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"clean": "echo 'nothing to clean'",
|
|
24
|
+
"build": "echo 'nothing to build'",
|
|
25
|
+
"check": "echo 'nothing to check'"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tall-stack
|
|
3
|
+
description: Deep knowledge about the TALL stack - Tailwind CSS, Alpine.js, Laravel, Livewire 4, and Flux UI. Use when working on TALL stack projects, Livewire components, Blade views, Alpine interactivity, or Flux UI components.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TALL Stack Reference
|
|
7
|
+
|
|
8
|
+
## Architecture
|
|
9
|
+
|
|
10
|
+
The TALL stack connects:
|
|
11
|
+
- **Laravel** as the backend framework (routing, controllers, models, migrations, Eloquent)
|
|
12
|
+
- **Livewire 4** for reactive server-rendered components (replaces traditional AJAX)
|
|
13
|
+
- **Alpine.js** for lightweight client-side interactivity (toggles, dropdowns, transitions)
|
|
14
|
+
- **Tailwind CSS** for utility-first styling
|
|
15
|
+
- **Flux UI** for pre-built, accessible Livewire/Alpine components
|
|
16
|
+
|
|
17
|
+
### When to use Livewire vs Alpine
|
|
18
|
+
|
|
19
|
+
- **Livewire**: Server state, database operations, form validation, real-time updates, anything needing PHP
|
|
20
|
+
- **Alpine**: Client-only UI state (dropdowns, modals, tabs, animations), no server round-trip needed
|
|
21
|
+
- **Both together**: Alpine handles instant UI feedback, Livewire handles persistence
|
|
22
|
+
|
|
23
|
+
## Livewire 4
|
|
24
|
+
|
|
25
|
+
### Component Structure
|
|
26
|
+
|
|
27
|
+
```php
|
|
28
|
+
<?php
|
|
29
|
+
|
|
30
|
+
namespace App\Livewire;
|
|
31
|
+
|
|
32
|
+
use Livewire\Component;
|
|
33
|
+
use Livewire\Attributes\Validate;
|
|
34
|
+
use Livewire\Attributes\Locked;
|
|
35
|
+
use Livewire\Attributes\Url;
|
|
36
|
+
use Livewire\Attributes\Computed;
|
|
37
|
+
use Livewire\Attributes\On;
|
|
38
|
+
|
|
39
|
+
class EditProfile extends Component
|
|
40
|
+
{
|
|
41
|
+
#[Validate('required|string|max:255')]
|
|
42
|
+
public string $name = '';
|
|
43
|
+
|
|
44
|
+
#[Validate('required|email')]
|
|
45
|
+
public string $email = '';
|
|
46
|
+
|
|
47
|
+
#[Locked]
|
|
48
|
+
public int $userId;
|
|
49
|
+
|
|
50
|
+
#[Url]
|
|
51
|
+
public string $tab = 'general';
|
|
52
|
+
|
|
53
|
+
#[Computed]
|
|
54
|
+
public function user(): User
|
|
55
|
+
{
|
|
56
|
+
return User::find($this->userId);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public function save(): void
|
|
60
|
+
{
|
|
61
|
+
$this->validate();
|
|
62
|
+
$this->user->update([
|
|
63
|
+
'name' => $this->name,
|
|
64
|
+
'email' => $this->email,
|
|
65
|
+
]);
|
|
66
|
+
$this->dispatch('profile-updated');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public function render()
|
|
70
|
+
{
|
|
71
|
+
return view('livewire.edit-profile');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Key Livewire 4 Attributes
|
|
77
|
+
|
|
78
|
+
| Attribute | Purpose |
|
|
79
|
+
|-----------|---------|
|
|
80
|
+
| `#[Validate('rules')]` | Inline validation rules on properties |
|
|
81
|
+
| `#[Locked]` | Property cannot be modified from the frontend (security) |
|
|
82
|
+
| `#[Url]` | Syncs property with URL query string |
|
|
83
|
+
| `#[Computed]` | Cached computed property (auto-memoized per request) |
|
|
84
|
+
| `#[On('event-name')]` | Listen for dispatched events |
|
|
85
|
+
| `#[Layout('layouts.app')]` | Set the layout for full-page components |
|
|
86
|
+
| `#[Title('Page Title')]` | Set the page title |
|
|
87
|
+
| `#[Lazy]` | Lazy-load the component (placeholder until visible) |
|
|
88
|
+
| `#[Isolate]` | Prevent this component from re-rendering when sibling components update |
|
|
89
|
+
|
|
90
|
+
### Livewire 4 Lifecycle
|
|
91
|
+
|
|
92
|
+
1. `mount()` -- Called once when component is first instantiated
|
|
93
|
+
2. `hydrate()` -- Called on every subsequent request (after deserialization)
|
|
94
|
+
3. `boot()` -- Called on every request (before mount on first, before hydrate on subsequent)
|
|
95
|
+
4. `updating{Property}()` / `updated{Property}()` -- Before/after property changes
|
|
96
|
+
5. `render()` -- Returns the Blade view
|
|
97
|
+
6. `dehydrate()` -- Called before serialization for the response
|
|
98
|
+
|
|
99
|
+
### Common Patterns
|
|
100
|
+
|
|
101
|
+
**Form handling:**
|
|
102
|
+
```php
|
|
103
|
+
use Livewire\Attributes\Validate;
|
|
104
|
+
|
|
105
|
+
#[Validate('required|string|max:255')]
|
|
106
|
+
public string $title = '';
|
|
107
|
+
|
|
108
|
+
public function save(): void
|
|
109
|
+
{
|
|
110
|
+
$this->validate();
|
|
111
|
+
Post::create(['title' => $this->title]);
|
|
112
|
+
$this->reset('title');
|
|
113
|
+
$this->dispatch('post-created');
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**File uploads:**
|
|
118
|
+
```php
|
|
119
|
+
use Livewire\WithFileUploads;
|
|
120
|
+
|
|
121
|
+
class UploadPhoto extends Component
|
|
122
|
+
{
|
|
123
|
+
use WithFileUploads;
|
|
124
|
+
|
|
125
|
+
#[Validate('image|max:1024')]
|
|
126
|
+
public $photo;
|
|
127
|
+
|
|
128
|
+
public function save(): void
|
|
129
|
+
{
|
|
130
|
+
$this->validate();
|
|
131
|
+
$this->photo->store('photos');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Pagination:**
|
|
137
|
+
```php
|
|
138
|
+
use Livewire\WithPagination;
|
|
139
|
+
|
|
140
|
+
class UserList extends Component
|
|
141
|
+
{
|
|
142
|
+
use WithPagination;
|
|
143
|
+
|
|
144
|
+
public function render()
|
|
145
|
+
{
|
|
146
|
+
return view('livewire.user-list', [
|
|
147
|
+
'users' => User::paginate(10),
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Real-time with events:**
|
|
154
|
+
```php
|
|
155
|
+
// Dispatching
|
|
156
|
+
$this->dispatch('post-created', id: $post->id);
|
|
157
|
+
|
|
158
|
+
// Listening
|
|
159
|
+
#[On('post-created')]
|
|
160
|
+
public function handlePostCreated(int $id): void
|
|
161
|
+
{
|
|
162
|
+
// React to the event
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Hydration Rules (Critical)
|
|
167
|
+
|
|
168
|
+
- Only `public` properties are sent to the frontend and back
|
|
169
|
+
- Use `#[Locked]` for IDs and sensitive data that should not be tampered with
|
|
170
|
+
- Eloquent models as properties: Livewire re-fetches by ID on each request
|
|
171
|
+
- Collections, arrays, and simple types are serialized/deserialized directly
|
|
172
|
+
- If a property cannot be serialized, use `#[Computed]` instead
|
|
173
|
+
|
|
174
|
+
## Alpine.js Patterns
|
|
175
|
+
|
|
176
|
+
### Basic Interactivity
|
|
177
|
+
```html
|
|
178
|
+
<div x-data="{ open: false }">
|
|
179
|
+
<button @click="open = !open">Toggle</button>
|
|
180
|
+
<div x-show="open" x-transition>
|
|
181
|
+
Content
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### With Livewire (@entangle)
|
|
187
|
+
```html
|
|
188
|
+
<div x-data="{ tab: @entangle('tab') }">
|
|
189
|
+
<button @click="tab = 'general'" :class="{ 'active': tab === 'general' }">
|
|
190
|
+
General
|
|
191
|
+
</button>
|
|
192
|
+
<button @click="tab = 'security'" :class="{ 'active': tab === 'security' }">
|
|
193
|
+
Security
|
|
194
|
+
</button>
|
|
195
|
+
</div>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Note: `@entangle` syncs Alpine state with Livewire server state. Use `@entangle('property').live` for real-time sync without waiting for Livewire requests.
|
|
199
|
+
|
|
200
|
+
### Alpine Stores (shared state)
|
|
201
|
+
```html
|
|
202
|
+
<script>
|
|
203
|
+
Alpine.store('sidebar', { open: true });
|
|
204
|
+
</script>
|
|
205
|
+
|
|
206
|
+
<div x-data @click="$store.sidebar.open = !$store.sidebar.open">
|
|
207
|
+
Toggle Sidebar
|
|
208
|
+
</div>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Flux UI
|
|
212
|
+
|
|
213
|
+
Flux UI provides pre-built, accessible components for TALL stack projects.
|
|
214
|
+
|
|
215
|
+
### Common Components
|
|
216
|
+
```blade
|
|
217
|
+
{{-- Button --}}
|
|
218
|
+
<flux:button>Save Changes</flux:button>
|
|
219
|
+
<flux:button variant="primary">Submit</flux:button>
|
|
220
|
+
<flux:button variant="danger" wire:click="delete">Delete</flux:button>
|
|
221
|
+
|
|
222
|
+
{{-- Input --}}
|
|
223
|
+
<flux:input wire:model="name" label="Full Name" />
|
|
224
|
+
<flux:input wire:model="email" label="Email" type="email" />
|
|
225
|
+
|
|
226
|
+
{{-- Select --}}
|
|
227
|
+
<flux:select wire:model="role" label="Role">
|
|
228
|
+
<flux:option value="admin">Admin</flux:option>
|
|
229
|
+
<flux:option value="user">User</flux:option>
|
|
230
|
+
</flux:select>
|
|
231
|
+
|
|
232
|
+
{{-- Modal --}}
|
|
233
|
+
<flux:modal name="confirm-delete">
|
|
234
|
+
<flux:heading>Delete Item?</flux:heading>
|
|
235
|
+
<flux:text>This action cannot be undone.</flux:text>
|
|
236
|
+
<flux:button variant="danger" wire:click="delete">Confirm</flux:button>
|
|
237
|
+
</flux:modal>
|
|
238
|
+
|
|
239
|
+
<flux:button @click="$flux.modal('confirm-delete').show()">Delete</flux:button>
|
|
240
|
+
|
|
241
|
+
{{-- Table --}}
|
|
242
|
+
<flux:table>
|
|
243
|
+
<flux:columns>
|
|
244
|
+
<flux:column>Name</flux:column>
|
|
245
|
+
<flux:column>Email</flux:column>
|
|
246
|
+
<flux:column>Actions</flux:column>
|
|
247
|
+
</flux:columns>
|
|
248
|
+
<flux:rows>
|
|
249
|
+
@foreach($users as $user)
|
|
250
|
+
<flux:row :key="$user->id">
|
|
251
|
+
<flux:cell>{{ $user->name }}</flux:cell>
|
|
252
|
+
<flux:cell>{{ $user->email }}</flux:cell>
|
|
253
|
+
<flux:cell>
|
|
254
|
+
<flux:button size="sm" wire:click="edit({{ $user->id }})">Edit</flux:button>
|
|
255
|
+
</flux:cell>
|
|
256
|
+
</flux:row>
|
|
257
|
+
@endforeach
|
|
258
|
+
</flux:rows>
|
|
259
|
+
</flux:table>
|
|
260
|
+
|
|
261
|
+
{{-- Card --}}
|
|
262
|
+
<flux:card>
|
|
263
|
+
<flux:heading>Dashboard</flux:heading>
|
|
264
|
+
<flux:text>Welcome back.</flux:text>
|
|
265
|
+
</flux:card>
|
|
266
|
+
|
|
267
|
+
{{-- Tabs --}}
|
|
268
|
+
<flux:tabs wire:model="tab">
|
|
269
|
+
<flux:tab name="general">General</flux:tab>
|
|
270
|
+
<flux:tab name="security">Security</flux:tab>
|
|
271
|
+
</flux:tabs>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Flux UI Guidelines
|
|
275
|
+
- Always use Flux components when available instead of raw HTML + Tailwind
|
|
276
|
+
- Flux components integrate directly with Livewire via `wire:model` and `wire:click`
|
|
277
|
+
- Use Flux modals with the `$flux.modal()` API for programmatic control
|
|
278
|
+
- Flux handles accessibility (ARIA attributes, keyboard navigation) automatically
|
|
279
|
+
|
|
280
|
+
## File Structure Conventions
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
app/
|
|
284
|
+
Livewire/ # Livewire components (PHP classes)
|
|
285
|
+
Users/
|
|
286
|
+
UserTable.php
|
|
287
|
+
EditUser.php
|
|
288
|
+
Models/ # Eloquent models
|
|
289
|
+
resources/
|
|
290
|
+
views/
|
|
291
|
+
livewire/ # Blade views for Livewire components
|
|
292
|
+
users/
|
|
293
|
+
user-table.blade.php
|
|
294
|
+
edit-user.blade.php
|
|
295
|
+
components/ # Blade components (non-Livewire)
|
|
296
|
+
layouts/
|
|
297
|
+
app.blade.php
|
|
298
|
+
routes/
|
|
299
|
+
web.php # Routes (Livewire full-page components use Route::get)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Common Gotchas
|
|
303
|
+
|
|
304
|
+
1. **wire:model vs wire:model.live**: Default `wire:model` only syncs on form submit. Use `.live` for real-time sync, `.blur` for sync on blur.
|
|
305
|
+
2. **Alpine + Livewire timing**: Alpine runs client-side instantly; Livewire needs a server round-trip. Use `wire:loading` for feedback.
|
|
306
|
+
3. **Computed properties**: Always use `#[Computed]` for expensive queries. They're memoized per-request.
|
|
307
|
+
4. **Security**: Always use `#[Locked]` on IDs and values that should not be tampered with from the browser.
|
|
308
|
+
5. **Nested components**: Child Livewire components re-render independently. Use events (`dispatch`/`#[On]`) to communicate.
|
|
309
|
+
6. **Flux UI + dark mode**: Flux respects Tailwind's `dark:` prefix. Ensure your layout has the dark mode class toggling.
|