Commit graph

9 commits

Author SHA1 Message Date
Paulo Castellano
2f047f2f7e Fix workspace name-only update rejected by required brand fields
The Workspace and Brand settings tabs both submit to the same
updateSettings endpoint, but the Workspace tab sends only the name.
UpdateWorkspaceRequest marked brand_font and image_style as required,
so the name-only request failed validation silently (the form only
renders the name error). Mark both brand fields as sometimes|required
so a name-only update succeeds while the Brand tab still validates them.

Closes #74
2026-06-02 08:45:11 -03:00
Paulo Castellano
148a2f432f feat: AI image generation pipeline and post creation overhaul
Core changes:
- Replace Unsplash slide pipeline with gpt-image-2 via Laravel AI SDK.
  New AiImageClient builds prompts from a Blade template seeded by the
  workspace's ImageStyle enum, content language, brand color (mapped to a
  human-readable name via HexColorName helper) and brand description.
- Drop Template B from TemplateImageGenerator: every slide now renders as
  Template A (full-bleed photo + bottom gradient + white/grey overlay).
  Removes renderTemplateB, roundCorners, blendHex, ensureContrast and the
  closing-slide pipeline.
- StreamPostCreation creates the Post directly and dispatches
  PostCreationReady with post_id; the wizard kills its preview step and
  redirects straight to the post editor on completion. Finalize endpoint
  removed.
- New Workspace.image_style enum field with an 8-option visual picker
  shared by /workspaces/create and /settings/workspace/brand via a single
  BrandForm component (autofill is a prop). 8 sample webp thumbs ship
  under public/images/branding/image-styles/.
- Media items gain optional source ('ai'|'unsplash'|'giphy') and
  source_meta (recipe needed to regenerate AI images later); the gallery
  picker tags Unsplash/Giphy attachments.
- Brand-color autofill: new CssColorFrequencyExtractor parses every
  hex/rgb/hsl value in the homepage CSS, clusters perceptually similar
  shades in CIE LAB (Delta E 76 < 12), filters neutrals and returns the
  most frequent cluster. Solves Tailwind/utility-CSS sites where no
  semantic --primary variable is exposed.
- Credits: gpt-image-2 metered at 15 credits/image (low quality default).
- Layout: AuthSplitLayout right column is sticky/h-svh so the form
  textarea growth no longer stretches the marketing slider.
- i18n cleanup: localized labels follow the no-em-dash convention.
2026-05-08 13:38:30 -03:00
Paulo Castellano
1e1519876d feat: replace legacy AI assistant with modular post content generation, review, and template management system 2026-05-03 09:36:50 -03:00
Paulo Castellano
8ba5877bdf feat: implement automated brand color extraction from web metadata, CSS, and logos with supporting database schema updates 2026-05-02 13:30:59 -03:00
Paulo Castellano
b3b59b4d13 refactor: remove onboarding flow, implement brand analysis services, and replace setup middleware with account readiness checks 2026-04-16 23:05:51 -03:00
Paulo Castellano
aaa4db1dbb feat: add workspace content_language setting for AI generation
Users can now pick the language AI uses for captions, hashtags,
descriptions, AND on-image / on-screen text — independent of the
app UI locale. A user with pt-BR UI who writes for an English
audience gets English content, and vice versa.

- New content_language column on workspaces (added to the existing
  create_workspaces_table migration since we're pre-production),
  defaulting to 'en'. Fillable on the model.
- Dropdown in Settings → Workspace → Brand, side-by-side with Tone.
  Options: English, Português (Brasil), Español. Short helper copy
  explains what the setting controls.
- Validation: sometimes|string|in:en,pt-BR,es — optional on update
  so existing callers that don't post the field keep the current
  value (DB default handles fresh workspaces).
- System prompt now uses $content_language (sourced from the
  workspace) instead of app()->getLocale(). The rule was also
  strengthened: the AI must always write in this language regardless
  of what language the user types instructions in, and must instruct
  image/video tools to render on-media text in this language too.
- Image and video Blade prompts received a new line forcing any text
  rendered inside the generated media to use $content_language.
- GenerateImage tool and VideoGenerationService pass the workspace's
  content_language into their respective prompt templates.

Previously the caption would be in Portuguese (since it followed the
user's input language) but the image could render with English text
because there was no explicit constraint. Now the whole pipeline is
aligned to one per-workspace language.
2026-04-16 10:37:03 -03:00
Paulo Castellano
5493871b2d feat: add brand config to workspace and Blade prompt system for AI 2026-04-15 21:28:40 -03:00
Paulo Castellano
56b8c92e72 refactor: settings redesign, Spanish translations, language system, strict_types
Settings pages:
- Redesign layout to match Sendkit (max-w-4xl, space-y-12, Separator sections)
- Merge Members page into Workspace settings with Table, invite Dialog, ConfirmDeleteModal
- Add workspace logo upload/delete routes and controller methods
- Translate all hardcoded strings in Workspace.vue modals

Language system:
- Drop languages table, replace language_id FK with locale string column on users
- Create config/languages.php for available languages and default locale
- Add Spanish (es) translations (13 files)
- Simplify HandleInertiaRequests, ProfileController, RegisteredUserController

Code quality:
- Add declare(strict_types=1) to all PHP files
- Fix MastodonPublisher using wrong attribute (filename -> original_filename)
- Fix HasMediaTest for new has_photo/photo_url accessors
- Fix PublishToSocialPlatformTest type error revealed by strict_types
- Remove orphaned Language model from AppServiceProvider morph map
- Update User TypeScript interface (has_photo, photo_url, locale)
- Eager load media relation on workspaces to prevent N+1
- Add 8 new tests for workspace logo upload/delete
- Update workspace settings test to assert members/invitations props

All 710 tests passing.
2026-03-30 00:20:43 -03:00
Paulo Castellano
a926033d06 refactor: organize middleware/requests into App/ subdirs, add Resources, fix auth routes
- Move middleware to App/ subdir (HandleInertiaRequests, HandleAppearance,
  EnsureSubscribed, EnsureUserSetupIsComplete) matching Sendkit pattern
- Move all Form Requests into organized subdirs (App/Post, App/Workspace,
  App/Media, App/Invite, App/Settings, App/Auth)
- Create AuthUserResource and AuthWorkspaceResource for HandleInertiaRequests
  shared data (role inside currentWorkspace, matching Sendkit pattern)
- Split auth.php into 3 route groups (no middleware, guest, auth) matching
  Sendkit pattern exactly
- Fix UserFactory to include all nullable attributes (current_workspace_id,
  stripe_id, pm_type, pm_last_four, trial_ends_at)
- Fix SocialAccountResource (display_name not name)
- Update frontend for new auth prop structure
- 702 tests passing (2 pre-existing Mastodon failures)
2026-03-29 21:13:30 -03:00
Renamed from app/Http/Requests/UpdateWorkspaceRequest.php (Browse further)