* Remove the post-templates feature
Removes the browsable post-templates catalog end to end: routes
(app.post-templates.index/apply), controller, form requests, resource,
Registry/PostTemplateData/TemplateNotFoundException services, the
templates:report console command, the templates/ catalog (36 files
across en/es/pt-BR), the templates Index.vue page, the template card
on the post Create.vue choice screen, and its i18n keys across all 16
locales.
Also removes TemplateContextResolver, which fed catalog examples into
PostContentGenerator's AI prompt. The "examples" block (with its
heading) is removed from generator.blade.php rather than left empty;
AI generation no longer references the templates catalog.
The AI content-template system under app/Ai/Templates (image/tweet
card templates for the AI post wizard) and TemplateImageGenerator are
unrelated and untouched.
* Update FormRequest naming examples after the post-templates removal
CLAUDE.md and the Cursor rules cited ApplyPostTemplateRequest and
IndexPostTemplateRequest as naming examples. Both classes were deleted
with the feature, so the guidance pointed at files that no longer exist.
* Share the AiTemplate type between the create screen and the AI wizard
Create.vue and AiPostWizard.vue each declared their own AiTemplate
interface, and they had drifted: the wizard's carried
applies_brand_visuals, the create screen's did not. TypeScript treated
them as unrelated types with the same name, which surfaced as a TS2719
error where the templates prop is passed between them.
The backend sends seven fields (PostController::create), so the shared
declaration follows the payload rather than the union of the two copies.
* Default AI content language to Ukrainian
* Keep English as the default content language
* refactor: update content language handling in workspace creation
- Changed the default content language in CreateWorkspace to inherit the app's locale instead of defaulting to English.
- Updated related tests to reflect this change, ensuring that the content language aligns with the application's current locale settings.
- Cleaned up unnecessary code in the BrandTab component for better readability.
* Add Ukrainian as a full platform UI locale.
Wire uk into languages config with complete lang/uk translations and restore ContentLanguage↔UI parity checks.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Wire Ukrainian into dayjs and locale coverage tests.
Import the uk dayjs locale with Monday week-start and cover uk/uk-UA in brand autofill and SetLocale assertions.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Harden Ukrainian UI and AI content-language coverage.
Cover generator/reviewer/humanizer/image prompts, workspace pickers, persona labels, UI locale switch, and README for uk as a first-class platform language.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add Ukrainian Pinterest form strings after main merge.
Restore lang/uk posts.php key parity for the new pin title and destination link fields.
Co-authored-by: Cursor <cursoragent@cursor.com>
* List every supported UI language in the README.
Replace the abbreviated multi-language blurb with the full locale set from config.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Paulo Castellano <paulo@castellanos.llc>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Extract ContentLanguageOption into @/types and use it for availableContentLanguages
across BrandForm, BrandTab, Brand, Create, and the LanguagePicker options, so the
englishName field that drives search is visible to TypeScript instead of being
dropped silently by the pass-through prop types.
- Add BrandAnalyzerTest: assert the language schema enum equals the full 15-language
set (guards against it shrinking back to a hardcoded subset behind ::fake()) and
that instructions() lists every code.
- Isolate the "LLM language wins" autofill test by declaring the page as `en` while
the LLM returns `de`, so it actually proves mergeLlm precedence instead of both
paths agreeing.
- Cover SetLocale's cookie side effect: the default locale cookie is set on an
invalid/absent cookie and left untouched for a valid locale.
Replace the 'single'/'carousel' string format threaded through the AI
generation path with a typed GeneratorFormat enum (PostContentGenerator,
PostContentHumanizer, PostContentStreamer, StreamPostCreation,
RunGenerateNode, deriveFormat).
Remove generatorFormat() from AiContentTemplate and the three templates —
it conflated style and format (returning 'tweet_card'). The tweet-card
check now lives on ContentStyle via isTweetCard(), and the humanizer skip
on ContentStyle::humanizes(), so the style declares its own behaviour
instead of callers comparing magic strings.
Replace free-text brand_tone/brand_voice_notes with a single structured
brand_voice_traits JSON column backed by the BrandVoiceTrait enum, exposed
as choice-chip pills in the brand settings UI and autofillable from a site.
Brand voice and visuals become per-automation toggles on the Generate node.
Unify the image controls into one 0-10 picker (0 = text-only, 1 = single,
2+ = carousel) and feed the generator the most restrictive selected network
so copy fits every platform. Pass that same platform context through the
humanizer pass — extracted into a shared ResolvesPlatformCopyBudget trait —
so the rewrite can no longer drift past the character cap the generator
respected, in both the automation and manual creation flows.
Persist the trigger node's schedule editor fields on save (they were
silently dropped by validated() for lacking validation rules).
Replaces the vague "intro → development → conclusion" guidance in the
generator prompt with an explicit per-slide arc: open with a specific
real urgent problem, develop the idea while creating swipe motivation,
prove with concrete evidence (result/before-after/BTS/learning), and
close with a single specific next action. Includes a slide_count to
role-distribution table (2 through 6+ slides).
Adds a `role` enum field to each slide in the carousel JSON schema
(hook, development, proof, cta) so the structure is enforced via SDK
not just prompt text. Field is additive — handleCarousel still reads
only title/body/image_keywords, so consumers are unaffected.