smwks/tallium 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

smwks/tallium

Composer 安装命令:

composer create-project smwks/tallium

包简介

A TALL stack starter kit, focus on livewire single file components. AI coding friendly.

README 文档

README

A streamlined TALL stack starter kit for Laravel, built on Livewire single-file components. Forked from the official Laravel Livewire starter kit and refined for clarity -- especially when working with AI coding agents.

What's Included

  • Laravel 13 with Fortify authentication (login, registration, password reset, email verification, two-factor auth)
  • Livewire 4 with single-file page components (Route::livewire())
  • Flux UI component library
  • Tailwind CSS 4 with dark mode
  • TypeScript and Vite

What Changed From the Official Starter Kit

TALLium consolidates the official starter kit to reduce pattern variability and improve AI coding clarity:

  • Single layout per context -- one app.blade.php (authenticated), one auth.blade.php (auth forms), one guest.blade.php (public). No layout variants (sidebar/header, card/simple/split).
  • Layouts are self-contained -- each layout is a complete HTML document. No scattered partials to trace through.
  • Pages live in resources/views/pages/ -- routed via Route::livewire() or Route::view(). Consistent, predictable file locations.
  • Routes consolidated into routes/web.php -- no separate routes/settings.php.
  • Dynamic brand logo -- generates a monogram from config('app.name') instead of a hardcoded SVG.
  • Removed app/Livewire/Actions/ -- logout is handled inline. Less indirection.

Getting Started

# Clone and install
laravel new my-app --using=smwks/tallium

The composer setup script handles: dependency installation, .env creation, key generation, database migration, npm install, and asset build.

Development Server

composer dev

Starts the Laravel dev server, queue worker, log tail (Pail), and Vite in parallel.

Default test credentials (from seeder):

Other Commands

composer lint          # Fix code style with Pint
composer lint:check    # Check code style without fixing
composer test          # Run linter + test suite

Project Structure

routes/web.php                              # All routes
resources/views/
  layouts/
    app.blade.php                           # Authenticated layout (sidebar nav)
    auth.blade.php                          # Auth form layout (login, register, etc.)
    guest.blade.php                         # Public/guest layout
    partials/head.blade.php                 # Shared <head> content
  pages/
    welcome.blade.php                       # Landing page
    dashboard.blade.php                     # Dashboard
    settings/
      profile.blade.php                     # Profile settings (Livewire)
      security.blade.php                    # Security settings (password + 2FA)
      appearance.blade.php                  # Appearance settings (Livewire)
      container.blade.php                   # Settings layout wrapper
      delete-user-form.blade.php            # Account deletion form
      partials/heading.blade.php            # Settings page header
  components/
    app-logo.blade.php                      # Sidebar brand component
    app-logo-icon.blade.php                 # Dynamic monogram icon
    desktop-user-menu.blade.php             # User dropdown menu

License

MIT

Building TALLium from the Official Starter Kit

This section describes every change needed to transform the official Laravel Livewire starter kit into TALLium. Apply these steps in order to a fresh clone of the upstream kit.

Goal: Consolidate layout variants, enforce a single layout per context, move all pages under resources/views/pages/, consolidate routes into routes/web.php, replace the hardcoded brand SVG with a dynamic monogram, and remove indirection (Logout action class, multiple layout shims).

Overview of changes

Kind Count Summary
Delete 7 Logout action, 2 app layout variants, 3 auth layout variants, settings route file
Rename 5 head partial, dashboard, settings layout→container, settings heading, welcome
Modify 9 .gitignore, composer.json, seeder, 2 components, 2 layouts, 3 settings pages, routes/web.php
Create 1 layouts/guest.blade.php

Step 1 — Delete files

Remove these files entirely (they are replaced by inlined or consolidated alternatives):

app/Livewire/Actions/Logout.php
resources/views/layouts/app/header.blade.php
resources/views/layouts/app/sidebar.blade.php
resources/views/layouts/auth/card.blade.php
resources/views/layouts/auth/simple.blade.php
resources/views/layouts/auth/split.blade.php
routes/settings.php
git rm app/Livewire/Actions/Logout.php
git rm resources/views/layouts/app/header.blade.php
git rm resources/views/layouts/app/sidebar.blade.php
git rm resources/views/layouts/auth/card.blade.php
git rm resources/views/layouts/auth/simple.blade.php
git rm resources/views/layouts/auth/split.blade.php
git rm routes/settings.php

Why: The Logout action class is replaced by an inline form in the layout. The three auth layout variants (card/simple/split) are collapsed into a single auth.blade.php. The two app layout variants (header/sidebar) are collapsed into a single app.blade.php. Settings routes are merged into routes/web.php.

Step 2 — Rename files (preserving git history)

First create the destination directories:

mkdir -p resources/views/layouts/partials
mkdir -p resources/views/pages/settings/partials

Then rename:

git mv resources/views/partials/head.blade.php \
       resources/views/layouts/partials/head.blade.php

git mv resources/views/dashboard.blade.php \
       resources/views/pages/dashboard.blade.php

git mv resources/views/pages/settings/layout.blade.php \
       resources/views/pages/settings/container.blade.php

git mv resources/views/partials/settings-heading.blade.php \
       resources/views/pages/settings/partials/heading.blade.php

git mv resources/views/welcome.blade.php \
       resources/views/pages/welcome.blade.php

The last rename (welcome) also requires content changes — covered in Step 5.

Step 3 — Modify small files

.gitignore

Add one line at the end:

+/.claude

composer.json

-    "name": "laravel/livewire-starter-kit",
+    "name": "smwks/tallium",
     "type": "project",
-    "description": "The official Laravel starter kit for Livewire.",
+    "description": "A TALL stack starter kit, focus on livewire single file components. AI coding friendly.",
-        "livewire/livewire": "^4.1"
+        "livewire/livewire": "^4.2"
     "require-dev": {
         "fakerphp/faker": "^1.24",
+        "laravel/boost": "^2",
         "laravel/pail": "^1.2.5",
         "laravel/pint": "^1.27",
-        "laravel/sail": "^1.53",
         "mockery/mockery": "^1.6",

database/seeders/DatabaseSeeder.php

Add an explicit password so the dev seeder produces a known credential:

         User::factory()->create([
             'name' => 'Test User',
             'email' => 'test@example.com',
+            'password' => bcrypt('password'),
         ]);

resources/views/components/app-logo-icon.blade.php

Replace the hardcoded brand SVG with a dynamic two-letter monogram generated from config('app.name'):

-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 42" {{ $attributes }}>
-    <path
-        fill="currentColor"
-        fill-rule="evenodd"
-        clip-rule="evenodd"
-        d="M17.2 5.633 8.6.855 0 5.633v26.51l16.2 9 ..."
-    />
-</svg>
+<?php
+$letters = Str::of(config('app.name'))->substr(0, 2)->upper();
+?>
+<svg {{ $attributes }} viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="App Logo">
+    <rect x="4" y="4" width="56" height="56" rx="12" stroke="currentColor" stroke-width="2.5"/>
+    <text x="32" y="36" text-anchor="middle" dominant-baseline="central" font-family="ui-sans-serif, system-ui, sans-serif" font-size="26" font-weight="700" fill="currentColor">{{ $letters }}</text>
+</svg>

resources/views/components/app-logo.blade.php

Remove the @props/@if toggle. The component is always used in sidebar context now, so always renders flux:sidebar.brand. Name comes from config('app.name') rather than hardcoded:

-@props([
-    'sidebar' => false,
-])
-
-@if($sidebar)
-    <flux:sidebar.brand name="Laravel Starter Kit" {{ $attributes }}>
-        <x-slot name="logo" class="flex aspect-square size-8 items-center justify-center rounded-md bg-accent-content text-accent-foreground">
-            <x-app-logo-icon class="size-5 fill-current text-white dark:text-black" />
-        </x-slot>
-    </flux:sidebar.brand>
-@else
-    <flux:brand name="Laravel Starter Kit" {{ $attributes }}>
-        <x-slot name="logo" class="flex aspect-square size-8 items-center justify-center rounded-md bg-accent-content text-accent-foreground">
-            <x-app-logo-icon class="size-5 fill-current text-white dark:text-black" />
-        </x-slot>
-    </flux:brand>
-@endif
+<flux:sidebar.brand name="{{ config('app.name') }}" {{ $attributes }}>
+    <x-slot name="logo" class="flex aspect-square size-8 items-center justify-center rounded-md bg-accent-content text-accent-foreground">
+        <x-app-logo-icon class="size-5 fill-current text-white dark:text-black" />
+    </x-slot>
+</flux:sidebar.brand>

resources/views/pages/settings/appearance.blade.php

Two substitutions — updated include path and component name:

-    @include('partials.settings-heading')
+    @include('pages.settings.partials.heading')

     <flux:heading class="sr-only">{{ __('Appearance settings') }}</flux:heading>

-    <x-pages::settings.layout ...>
+    <x-pages::settings.container ...>
         ...
-    </x-pages::settings.layout>
+    </x-pages::settings.container>

resources/views/pages/settings/profile.blade.php

Same two substitutions:

-    @include('partials.settings-heading')
+    @include('pages.settings.partials.heading')

     <flux:heading class="sr-only">{{ __('Profile settings') }}</flux:heading>

-    <x-pages::settings.layout :heading="__('Profile')" :subheading="__('Update your name and email address')">
+    <x-pages::settings.container :heading="__('Profile')" :subheading="__('Update your name and email address')">
         ...
-    </x-pages::settings.layout>
+    </x-pages::settings.container>

resources/views/pages/settings/security.blade.php

Same two substitutions:

-    @include('partials.settings-heading')
+    @include('pages.settings.partials.heading')

     <flux:heading class="sr-only">{{ __('Security settings') }}</flux:heading>

-    <x-pages::settings.layout :heading="__('Update password')" :subheading="__('Ensure your account is using a long, random password to stay secure')">
+    <x-pages::settings.container :heading="__('Update password')" :subheading="__('Ensure your account is using a long, random password to stay secure')">
         ...
-    </x-pages::settings.layout>
+    </x-pages::settings.container>

Step 4 — Replace layout files

resources/views/layouts/auth.blade.php

The old file was a one-line shim delegating to a vendor component (x-layouts::auth.simple). Replace it with a self-contained HTML document:

-<x-layouts::auth.simple :title="$title ?? null">
-    {{ $slot }}
-</x-layouts::auth.simple>

Full replacement content:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
<head>
    @include('layouts.partials.head')
</head>
<body class="min-h-screen bg-white antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
<div class="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
    <div class="flex w-full max-w-sm flex-col gap-2">
        <a href="{{ route('home') }}" class="flex flex-col items-center gap-2 font-medium" wire:navigate>
            <span class="flex h-9 w-9 mb-1 items-center justify-center rounded-md">
                <x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
            </span>
            <span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
        </a>
        <div class="flex flex-col gap-6">
            {{ $slot }}
        </div>
    </div>
</div>
@fluxScripts
</body>
</html>

Note: @include('layouts.partials.head') resolves to resources/views/layouts/partials/head.blade.php (the renamed partial from Step 2).

resources/views/layouts/app.blade.php

The old file was a one-line shim delegating to x-layouts::app.sidebar. Replace it with a self-contained HTML document. Key additions vs the old vendor sidebar layout:

  • @include('layouts.partials.head') instead of @include('partials.head')
  • Lower nav section (commented out placeholder)
  • Mobile user dropdown uses inline logout form (no Logout action class)
  • @persist('toast') block with <flux:toast /> so Flux toast notifications work
-<x-layouts::app.sidebar :title="$title ?? null">
-    <flux:main>
-        {{ $slot }}
-    </flux:main>
-</x-layouts::app.sidebar>

Full replacement content:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
<head>
    @include('layouts.partials.head')
</head>
<body class="min-h-screen bg-white dark:bg-zinc-800">
<flux:sidebar sticky collapsible="mobile" class="border-e border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900">
    <flux:sidebar.header>
        <x-app-logo :sidebar="true" href="{{ route('dashboard') }}" wire:navigate />
        <flux:sidebar.collapse class="lg:hidden" />
    </flux:sidebar.header>

    <flux:sidebar.nav>
        <flux:sidebar.group :heading="__('Platform')" class="grid">
            <flux:sidebar.item icon="home" :href="route('dashboard')" :current="request()->routeIs('dashboard')" wire:navigate>
                {{ __('Dashboard') }}
            </flux:sidebar.item>
        </flux:sidebar.group>
    </flux:sidebar.nav>

    <flux:spacer />

    <flux:sidebar.nav>
        {{--
        <flux:sidebar.item icon="folder-git-2" href="#" target="_blank">
            {{ __('Lower Navigation Item') }}
        </flux:sidebar.item>
        --}}

    </flux:sidebar.nav>

    <x-desktop-user-menu class="hidden lg:block" :name="auth()->user()->name" />
</flux:sidebar>


<!-- Mobile User Menu -->
<flux:header class="lg:hidden">
    <flux:sidebar.toggle class="lg:hidden" icon="bars-2" inset="left" />

    <flux:spacer />

    <flux:dropdown position="top" align="end">
        <flux:profile
            :initials="auth()->user()->initials()"
            icon-trailing="chevron-down"
        />

        <flux:menu>
            <flux:menu.radio.group>
                <div class="p-0 text-sm font-normal">
                    <div class="flex items-center gap-2 px-1 py-1.5 text-start text-sm">
                        <flux:avatar
                            :name="auth()->user()->name"
                            :initials="auth()->user()->initials()"
                        />

                        <div class="grid flex-1 text-start text-sm leading-tight">
                            <flux:heading class="truncate">{{ auth()->user()->name }}</flux:heading>
                            <flux:text class="truncate">{{ auth()->user()->email }}</flux:text>
                        </div>
                    </div>
                </div>
            </flux:menu.radio.group>

            <flux:menu.separator />

            <flux:menu.radio.group>
                <flux:menu.item :href="route('profile.edit')" icon="cog" wire:navigate>
                    {{ __('Settings') }}
                </flux:menu.item>
            </flux:menu.radio.group>

            <flux:menu.separator />

            <form method="POST" action="{{ route('logout') }}" class="w-full">
                @csrf
                <flux:menu.item
                    as="button"
                    type="submit"
                    icon="arrow-right-start-on-rectangle"
                    class="w-full cursor-pointer"
                    data-test="logout-button"
                >
                    {{ __('Log Out') }}
                </flux:menu.item>
            </form>
        </flux:menu>
    </flux:dropdown>
</flux:header>

<flux:main>
    {{ $slot }}
</flux:main>

@persist('toast')
    <flux:toast.group>
        <flux:toast />
    </flux:toast.group>
@endpersist

@fluxScripts
</body>
</html>

Step 5 — Modify resources/views/pages/welcome.blade.php

This file was renamed from resources/views/welcome.blade.php in Step 2.

The original file is a self-contained HTML document (DOCTYPE through </html>). After the rename, strip the entire HTML wrapper — everything from <!DOCTYPE html> down through and including the <body class="..."> opening tag — and replace it with <x-layouts::guest>. Similarly replace the closing </body>\n</html> with </x-layouts::guest>.

The body content (header nav, main SVG artwork, footer) is unchanged.

-<!DOCTYPE html>
-<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
-    <head>
-        ...fonts, favicon, inline <style> block...
-    </head>
-    <body class="bg-[#FDFDFC] dark:bg-[#0a0a0a] text-[#1b1b18] flex p-6 lg:p-8 items-center lg:justify-center min-h-screen flex-col">
+<x-layouts::guest>
         <header class="w-full lg:max-w-4xl max-w-[335px] text-sm mb-6 not-has-[nav]:hidden">
             ...
         </header>
         <main>
             ...
         </main>
         @if (Route::has('login'))
             <div class="h-14.5 hidden lg:block"></div>
         @endif
-    </body>
-</html>
+</x-layouts::guest>

Scripted transformation (Python):

with open('resources/views/pages/welcome.blade.php', 'r') as f:
    content = f.read()

body_start = content.find('<body ')
body_end = content.find('>\n', body_start) + 2
content = '<x-layouts::guest>\n' + content[body_end:]
content = content.replace('    </body>\n</html>\n', '</x-layouts::guest>\n')

with open('resources/views/pages/welcome.blade.php', 'w') as f:
    f.write(content)

Step 6 — Create resources/views/layouts/guest.blade.php

Extract the HTML wrapper that was removed from welcome.blade.php (Step 5) and use it as the new guest layout. The wrapper is the HTML document structure including the pre-built inline CSS that the welcome page ships with — it does not use @vite because the welcome page CSS is pre-built and bundled inline for zero-dependency serving.

with open('resources/views/welcome.blade.php', 'r') as f:
    content = f.read()

body_start = content.find('<body ')
body_end = content.find('>\n', body_start) + 2
header = content[:body_end]

with open('resources/views/layouts/guest.blade.php', 'w') as f:
    f.write(header + '    {{ $slot }}\n</body>\n</html>\n')

Order matters: run the script for guest.blade.php before running the script for welcome.blade.php, or keep a copy of the original file. Once the wrapper is stripped from welcome.blade.php the source for the inline styles is gone.

The resulting guest.blade.php has this shape (CSS block abbreviated):

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>{{ __('Welcome') }} - {{ config('app.name', 'Laravel') }}</title>
        <link rel="icon" href="/favicon.ico" sizes="any">
        <link rel="icon" href="/favicon.svg" type="image/svg+xml">
        <link rel="apple-touch-icon" href="/apple-touch-icon.png">
        <link rel="preconnect" href="https://fonts.bunny.net">
        <link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600" rel="stylesheet" />
        <style>
            /* pre-built inline Tailwind CSS — copied verbatim from original welcome.blade.php */
        </style>
    </head>
    <body class="bg-[#FDFDFC] dark:bg-[#0a0a0a] text-[#1b1b18] flex p-6 lg:p-8 items-center lg:justify-center min-h-screen flex-col">
    {{ $slot }}
</body>
</html>

Step 7 — Replace routes/web.php

The old file required routes/settings.php (deleted in Step 1) and used bare view names. Replace entirely with the consolidated version that uses pages.* view names and inlines all settings routes:

 <?php

 use Illuminate\Support\Facades\Route;
+use Laravel\Fortify\Features;

-Route::view('/', 'welcome')->name('home');
+Route::view('/', 'pages.welcome')->name('home');

-Route::middleware(['auth', 'verified'])->group(function () {
-    Route::view('dashboard', 'dashboard')->name('dashboard');
-});
+Route::view('dashboard', 'pages.dashboard')
+    ->middleware(['auth', 'verified'])
+    ->name('dashboard');
+
+Route::middleware(['auth'])->group(function () {
+    Route::redirect('settings', 'settings/profile');
+
+    Route::livewire('settings/profile', 'pages::settings.profile')->name('profile.edit');
+});
+
+Route::middleware(['auth', 'verified'])->group(function () {
+    Route::livewire('settings/appearance', 'pages::settings.appearance')->name('appearance.edit');

-require __DIR__.'/settings.php';
+    Route::livewire('settings/security', 'pages::settings.security')
+        ->middleware(
+            when(
+                Features::canManageTwoFactorAuthentication()
+                    && Features::optionEnabled(Features::twoFactorAuthentication(), 'confirmPassword'),
+                ['password.confirm'],
+                [],
+            ),
+        )
+        ->name('security.edit');
+});

Summary of final layout structure

Layout file Used by Includes
layouts/app.blade.php All authenticated pages layouts/partials/head · Flux sidebar · mobile user menu · @persist('toast')
layouts/auth.blade.php Login, register, password reset, 2FA layouts/partials/head
layouts/guest.blade.php Welcome / public pages Pre-built inline CSS (no Vite)
layouts/partials/head.blade.php Included by app and auth layouts Vite assets, meta tags

Gotcha: <flux:toast /> must be in the app layout

The Flux::toast() PHP helper dispatches a Livewire event. The <flux:toast /> component must be present in the layout to receive it. The old vendor sidebar layout included this inside @persist('toast'). When inlining the layout, it is easy to forget:

@persist('toast')
    <flux:toast.group>
        <flux:toast />
    </flux:toast.group>
@endpersist

This must appear in layouts/app.blade.php after <flux:main> and before @fluxScripts. Without it, calls like Flux::toast(variant: 'success', text: 'Saved.') produce no visible output.

smwks/tallium 适用场景与选型建议

smwks/tallium 是一款 基于 Blade 开发的 Composer 扩展包,目前已累计 8 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 02 月 18 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「framework」 「laravel」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 smwks/tallium 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 smwks/tallium 我们能提供哪些服务?
定制开发 / 二次开发

基于 smwks/tallium 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 8
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 24
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: Blade

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-02-18