Codync
免費、開源的 1:1 替代方案,取代 Grok Bot 與 Muse。從 iPhone、Mac 或 Linux 桌面將 Claude Code、Codex、Cursor 與 40+ 程式碼代理人作為持續機器人傳訊。群組聊天、主題串…
AI 翻譯
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Franken CMS is a CMS alternative powered by Laravel and FilamentPHP
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗A WordPress alternative for Laravel. Powered by FilamentPHP and the TALL stack.
Franken CMS gives you content management — posts, pages, taxonomies, menus, SEO, and a template field system — as a Laravel package. Build your app your way without being locked into a rigid structure.
Beta — Franken CMS is in active development. APIs may change before the stable release.
Full documentation is available at frankencms.com.
composer require frankencms/franken-cms
Then run the installer:
php artisan franken-cms:install
The installer will guide you through publishing config, running migrations, registering the Filament plugin, and setting up your theme.
Franken CMS ships with optional AI-assisted content generation (SEO titles, meta descriptions, teasers, alt text, blog post drafts) built on the laravel/ai SDK. It's entirely opt-in — the package is not required to run Franken CMS.
composer require laravel/ai:^1.0
Using Amazon Bedrock? laravel/ai 1.x no longer installs the AWS SDK, so add it yourself:
composer require aws/aws-sdk-php
Add your API key(s) to .env. Franken CMS reads provider credentials straight from config/ai.php, so no key is stored in the database:
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
GEMINI_API_KEY=
See vendor/laravel/ai/config/ai.php for the full list of supported providers (Azure, Bedrock, Cohere, DeepSeek, ElevenLabs, Groq, Jina, Mistral, OpenAI-compatible, OpenRouter, VoyageAI, xAI, and more). Optionally publish the config to customize it further:
php artisan vendor:publish --tag=ai-config
Ollama requires no API key. It's disabled by default to avoid advertising a "local" provider that may not actually be running; opt in explicitly in .env:
CMS_AI_ENABLE_OLLAMA=true
With the SDK installed and at least one provider configured, go to CMS Settings → Igor in the Filament admin panel and toggle Igor on. AI features activate only when all three are true: the SDK is installed, a provider has credentials, and the toggle is enabled.
Note: API keys are no longer stored in the database. If you're upgrading from an older Franken CMS version, remove any stale
cms_ai.api_keyrow from your settings table (or runphp artisan migrate:fresh) and set the key in.envinstead.
Text generation and image generation are configured independently in CMS Settings → Igor → Provider:
Text Generation — powers SEO titles, meta descriptions, teasers, alt text, and blog post drafts.
Image Generation — powers featured image generation (and any future image features).
default_for_images when that provider has credentials, falling back to your first configured image-capable provider otherwise — so a single OPENAI_API_KEY works with no selection at all.Both engines have a Test Model button that fires a minimal probe with the currently selected provider/model and reports either success or the provider's exact error. Use it whenever a model misbehaves — providers (notably OpenAI) list models in their API that your project may not have permission to invoke, and the only reliable way to know is a real call. The image test generates one small low-quality image, so it asks for confirmation first (your provider bills for it, typically a cent or two).
Igor can generate a post's featured image using the image engine above. Configure the feature itself in CMS Settings → Igor → Prompts → Featured Image Generation:
{title} and {excerpt} placeholders ({excerpt} is filled from the post's teaser field)Once enabled, a Generate with AI button appears next to the featured image upload when editing an existing post or page (it's hidden while creating a new one, since there's no record yet to attach the image to). The button opens a modal with the prompt pre-filled from your template, editable before generating. The generated image's aspect ratio follows the featured image aspect ratio configured in Media settings (21:9 and custom ratios fall back to 16:9), and it replaces the current featured image on success.
Franken CMS can generate og:image and twitter:image previews automatically for posts and pages, built on spatie/laravel-og-image. It's entirely opt-in — without the package, manual per-post uploads and the site default image keep working exactly as before.
composer require spatie/laravel-og-image
The franken-cms:install command will also offer to install and wire it up for you.
Templates are mapped by post type in config/franken-cms.php. Publish the config if you haven't (php artisan vendor:publish --tag=franken-cms-config) — the mappings ship commented out, so generation stays off until you enable them:
'og_image' => [
'templates' => [
'post' => 'theme.og-templates.post',
'page' => 'theme.og-templates.page',
],
],
Each template is a plain Blade view rendered on a 1200×630 canvas and receives $post — do not wrap it in <x-og-image> yourself, Franken CMS's component handles that.
The example theme ships two ready-made designs in theme/og-templates/:
post.blade.php — the "specimen card": category + specimen-number chip, gradient display title, author ⌁ date ⌁ read-time metadata, and the featured image seamed in with a lightning-bolt edge and suture stitches.page.blade.php — the "dossier card": the calmer sibling — page-path chip, site tagline, and a gradient bolt watermark when the page has no featured image.Both are fully self-contained (fonts and CSS live inside the template, no asset rebuild needed), scale the title to its length, honor each image's focal point, and degrade gracefully when data is missing — edit them freely, or use them as a reference for your own. Preview any page's card by appending ?ogimage to its URL; the image URL contains a content hash, so design changes bust caches automatically.
Drop <x-franken-og-image /> into your theme layout before </body> (the example theme ships with it already in place).
For each page, an image is resolved in this order: per-post uploaded image → mapped template → site default image → site-wide fallback template. The fallback template is opt-in — point og_image.default_template in config/franken-cms.php at a Blade view (the example theme ships one at theme.og-templates.default) and it generates an image for any page that would otherwise have none. Posts that opt into Twitter summary cards (instead of large-image cards) keep the classic manual og:image/twitter:image tag path.
Image generation needs Chrome and Node available on the server:
npm install puppeteer
npx puppeteer browsers install chrome
If that's not an option, set the following in .env to render via Cloudflare's Browser Rendering API instead:
CLOUDFLARE_API_TOKEN=
CLOUDFLARE_ACCOUNT_ID=
Cloudflare needs a publicly reachable URL. Its browser runs in Cloudflare's cloud and fetches your page to screenshot it, so it cannot see local domains like
https://myapp.test— screenshots fail with a network error. Use the local Chrome driver for local development (leave theCLOUDFLARE_*vars unset there) and enable Cloudflare in deployed environments.
For routes outside the CMS (hand-coded, non-post/page views), use <x-og-image> directly per Spatie's documentation.
FrankenCMS's OG image generation is scoped to CMS-managed pages, so on these non-CMS routes it keeps emitting its own classic tags (including a site default og:image, if one is configured). That means a page using <x-og-image> directly will end up with Spatie's generated tag alongside FrankenCMS's classic tag. Crawlers generally take the first og:image they encounter, so if that matters, either remove the site default or skip the <x-og-image> component on that page.
Append ?ogimage to any page URL to preview the generated image directly in the browser.
FrankenCMS automatically generates sitemaps for CMS content (sitemap-pages.xml, sitemap-posts.xml). It can also generate sitemap-routes.xml covering your application's own routes — the static pages you define in routes/web.php outside the CMS.
Enabled by default; toggle it under Settings → Sitemap → App Routes Sitemap.
A route is included automatically when it is a static GET route (no URI parameters), uses the web middleware, has no domain restriction, and is not:
auth, guest, verified, signed, or password.confirm middlewareRoute::redirect() (the target belongs in the sitemap, not the redirect)// Force-include a route the heuristics would skip, optionally with metadata
Route::middleware(['web', 'auth'])
->get('/members/welcome', WelcomeController::class)
->sitemap(priority: 0.9, changeFrequency: 'monthly');
// Exclude a route the heuristics would include
Route::view('/internal-demo', 'demo')->skipSitemap();
Allowed changeFrequency values: always, hourly, daily, weekly, monthly, yearly, never. Priority is 0.0–1.0.
Routes without overrides use the sitemap settings' default priority and change frequency. The Excluded Paths setting always wins — a URL matching an excluded pattern never appears, even with a ->sitemap() force-include. Routes with URI parameters can't be enumerated; point Custom Sitemaps at a hand-built sitemap for those.
Gate detection is alias- and known-middleware-based, not exhaustive — routes gated by policies, custom middleware classes, or custom middleware groups won't be recognized as gated automatically, so use ->skipSitemap() explicitly for those.
CMS content is routed by Laravel as usual (a Route::fallback() plus named page routes). Livewire can sit on top of that in two optional ways. Your theme layout needs @livewireStyles and @livewireScripts; the example theme already has both.
Your own Route::livewire() routes in routes/web.php keep working alongside the CMS, because the CMS fallback route is registered last.
wire:navigate)CMS_LIVEWIRE_NAVIGATE=true
# Optional: prefetch on hover (wire:navigate.hover)
CMS_LIVEWIRE_NAVIGATE_PREFETCH=true
Menu items, breadcrumbs, and the example theme's post, category, and tag links then fetch the next page and swap the body without a full reload. Links get wire:navigate only when they are internal and open in the same tab. External URLs, #anchors, mailto:/tel: links, file URLs (sitemap.xml, *.pdf) and the paths in franken-cms.livewire.navigate_except (by default admin, feed) always do a full page load.
Use the same rule on your own theme links:
<a href="{{ $url }}" @frankenNavigate($url)>…</a>
{{-- or --}}
<a href="{{ $url }}" {{ franken_navigate($url, $target) }}>…</a>
Things to know:
DOMContentLoaded runs only once. Listen for livewire:navigated instead.@persist('name') … @endpersist. Don't persist the header if it shows the active menu state.<head> tags are rendered server-side on every request and merged by Livewire, and crawlers always do full loads, so SEO output is unchanged.Any theme template can be a Livewire single-file component. Write it in the theme folder with the usual name (page-…, post-…), without a ⚡ prefix, and pick it per page in the admin like any other template:
{{-- resources/views/theme/page-newsletter.blade.php --}}
<?php
use FrankenCms\Livewire\Concerns\InteractsWithFrankenPage;
use Livewire\Component;
new class extends Component {
use InteractsWithFrankenPage; // provides $page / $post and $frankenFields
public string $email = '';
public function subscribe(): void
{
$this->validate(['email' => 'required|email']);
// ...
}
};
?>
<div>
<h1>@frankenText('hero.title', ['label' => 'Title', 'default' => 'Join the list'])</h1>
<form wire:submit="subscribe">
<input type="email" wire:model="email">
<button>Subscribe</button>
</form>
</div>
@franken* fields work as they do in plain templates: they show up in the editor, and their values are available on every render, including Livewire update requests.franken-cms.livewire.layout. If that isn't set, the example theme's theme.components.layouts.main.index is used when it exists, then Livewire's component_layout. A #[Layout] attribute on the component overrides all of these.WithPagination). The CMS passes only $page/$post, never the posts paginator.CMS_LIVEWIRE_PAGE_COMPONENTS=false.composer test
See CHANGELOG for version history.
See CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). See License File for more information.