Skip to content

javaabu/mediapicker v1.0 (next-gen) - #2

Open
n4ff4h wants to merge 14 commits into
Javaabu:mainfrom
n4ff4h:next-gen
Open

n4ff4h wants to merge 14 commits into
Javaabu:mainfrom
n4ff4h:next-gen

Conversation

@n4ff4h

@n4ff4h n4ff4h commented Nov 10, 2026

Copy link
Copy Markdown

javaabu/mediapicker v1.0 (next-gen)

This turns the unreleased version on main into v1.0 for the Javaabu next-gen stack. It was built on the fork n4ff4h/mediapicker (next-gen), tracked by n4ff4h#13. CI is green on every job: PHP 8.4/8.5 × Laravel 12/13 × lowest/stable, Pint with the javaabu/helpers guard, TypeScript (lint, format, typecheck, Vitest, dist freshness), and a run without javaabu/translatable. There are 180 PHPUnit tests and 34 Vitest tests.

What's in it

  • Foundations: PHP 8.4 and Laravel 12/13. javaabu/helpers, javaabu/activitylog v1 and javaabu/forms v1 are gone; it now depends on javaabu/forms v2, permissions v3, stencil, manifest and block-editor. Pint uses the Javaabu rules, and coverage goes to Codecov.
  • Routes, controller, RBAC:
    • Route::mediapicker([guard, prefix, name, middleware, controller]), with one library per guard.
    • An overridable controller whose actions all answer JSON (MediaResource).
    • view/edit/delete(_others)_media permissions registered through javaabu/permissions, and a MediaPolicy built on ResourcePolicy with ownership.
  • Library pages in Blade (forms v2) and Inertia React (mediapicker::Media/*, through manifest), with a committed dist for apps without Vite.
  • Pickers:
    • <x-mediapicker::picker> in Blade, React MediaPicker/MediaPickerField/MediaLibraryDialog, and a stencil MediaPickerField.
    • The LibraryMedia rule only accepts media the user may see.
    • syncAttachmentsFromRequest() saves a picker field to an attachment collection.
  • Conversions:
    • Fingerprinted attachment conversions in conversions/attachments: shared between models, never made twice, and no longer overwriting each other.
    • Queued, and only for the collections that need them.
    • WebP/AVIF copies and responsive widths, rendered by <x-mediapicker::image> and React <Picture>.
    • mediapicker:clean removes stale files per media.
  • Cropping: non-destructive crops to aspect-ratio presets that drive conversions. They can be set on upload (React) or on the edit page (both stacks).
  • Block editor: image (replacing the core URL image), gallery, file and mediaVideo blocks that pick from the library.
  • Translatable (optional): names, descriptions, alt text and captions per locale.
  • Dhivehi and RTL: lang/en.json and lang/dv.json, logical CSS throughout, and Thaana inputs.
  • Docs: 23 pages in docs/, llms.txt, a Laravel Boost guideline (in a verbatim block, rendered by Boost in tests) and skill, and AGENTS.md.

Upgrade notes (from the unreleased main)

Full guide: docs/upgrading.md.

  • Routes: Mediapicker::registerRoutes() is replaced by Route::mediapicker(), and the ?mode=picker page is gone.
  • Authorization: MediaOwner loses the can*Media() methods (the policy decides). userVisible()/findForController() become visibleTo($user)/inLibraryOf($user). Run php artisan permissions:sync.
  • Media model: Media no longer implements AdminModel or logs activity. $media->url() becomes $media->libraryUrl(), and getIcon() returns Lucide names.
  • File types: they come from mediapicker.file_types. pdf is now part of document, WebP and AVIF are images, and SVG is off by default.
  • Conversions: attachment conversion files moved. Run php artisan mediapicker:regenerate, then media-library:clean.
  • updateAttachmentMedia() now returns the whole collection in order and reorders existing attachments.
  • Views: the material-admin-26 views and their config keys are gone. Use views, layout and layout_section.

Bugs fixed on the way

  • updateAttachmentMedia() used a static counter that leaked between calls, and couldn't find media that were already attached.
  • Same-named conversions on different models overwrote each other's files.
  • mediapicker:clear --force had a broken option name.
  • Removing attachment conversions on media delete left an empty folder behind.

Before you merge or release

  • Codecov: make CODECOV_TOKEN available to the repo (uploads are rejected without it; CI doesn't fail).
  • Dhivehi review: a native speaker should check the terms in docs/advanced-usage/dhivehi-and-rtl.md#needs-review.
  • Docs: register with docs.javaabu.com (Register with Javaabu Docs (docs.javaabu.com) n4ff4h/mediapicker#12).
  • Release: tag v1.0 once the unreleased dependencies (forms v2, permissions v3, stencil, manifest, block-editor, translatable v3 on the fork) are released. Then drop the VCS repositories from composer.json.

🤖 Generated with Claude Code

n4ff4h and others added 14 commits November 5, 2026 20:00
…matrix and Codecov

- Require javaabu/forms ^2, manifest, permissions ^3, stencil and block-editor;
  drop javaabu/helpers, javaabu/activitylog v1 and javaabu/forms v1
- File types, mime types and upload sizes come from config (Support\FileTypes)
- Media no longer implements AdminModel or logs activity by itself
- Pint with the Javaabu rules, CI on PHP 8.4/8.5 x Laravel 12/13 x lowest/stable,
  coverage to Codecov, a guard against javaabu/helpers and settings ^1
- The old material-admin page tests are incomplete until the new views land (#9)

Refs #1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sions, JSON API

- Route::mediapicker([guard, prefix, name, middleware, controller]) replaces
  Mediapicker::registerRoutes(); each guard can have its own library, and URLs are
  built from the library the request came through
- The controller comes from config('mediapicker.media_controller') or the macro;
  every action answers JSON (MediaResource) or a Blade/Inertia page
- view/view_others/edit/edit_others/delete/delete_others_media registered through
  javaabu/permissions; MediaPolicy extends ResourcePolicy with ownership and is
  overridable from config, permissions.policies or Gate::policy()
- MediaOwner no longer carries the can*Media() checks; the policy decides
- Uploads take a type, details (name, description, alt, caption) and crops
- Attachment conversion files are removed before the media's attachments go

Closes #10

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mats, srcset, crops

- AttachmentConversion: usingCrop(), formats(['avif', 'webp']) and
  withResponsiveImages(); its file name carries a fingerprint of what it does, in
  the media's conversions/attachments folder (safe from spatie's own clean)
- Identical conversions of different models share one file; same-named ones that
  differ no longer overwrite each other; attaching media again processes nothing
- Generated sizes, formats and widths are recorded on the media; getAvailableUrl(),
  getFormatUrl() and getSrcset() on attachments, and <x-mediapicker::image> renders
  <picture> with modern sources, srcset, width and height
- Crops are stored per preset on the media (Cropper, PUT media/{id}/crops/{preset});
  a new crop regenerates only the conversions that use it
- mediapicker:clean removes files no attachment of the media uses any more
- The library thumbnail is non-queued; formats the image driver can't write are skipped

Refs #3, refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ld, cropper UI

- Blade pages (forms v2) and Inertia React pages (mediapicker::Media/*) through manifest
- <x-mediapicker::picker> with a framework-free picker dialog; React MediaPicker,
  MediaPickerField, MediaLibraryDialog, MediaCropper, Picture, StencilMediaPicker
- Stencil MediaPickerField and the MediaDetailsForm; LibraryMedia validation rule;
  syncAttachmentsFromRequest(); updateAttachmentMedia() keeps order and reorders
- Mediapicker::pickerConfig() and Mediapicker::image() for React
- Vitest for the API client, dialog and Blade field; tests/js/react.test.tsx still
  fails to load @javaabu/onu/media (to fix next)

Refs #2, refs #4, refs #9

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- image (replacing the core URL image), gallery, file and mediaVideo blocks for
  javaabu/block-editor: they pick from the library in the editor, keep the media id
  and a snapshot, and render the current media in Blade (snapshot in React)
- Block content only accepts media the editor may see (LibraryMedia)
- dist/ with the Blade picker, cropper and blocks for apps without Vite; CI checks it
- CI job for TypeScript: lint, format, typecheck, Vitest with coverage, dist
- React tests load Onu's media components; page and block tests

Closes #5

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Name, description, alt text and caption translate with javaabu/translatable's JSON
  storage: a migration adds nullable translations/lang to the media table, and
  Models\Translatable\Media is used when translatable is on and no model is configured
- Uploads, updates, the stencil details form and MediaResource take values per locale
- Media::url() becomes libraryUrl(), as translatable's own url() needs the name
- Feature detection also checks translatable's provider is loaded; a CI leg runs
  without the package
- Conversion fixtures use fit(Fit::Crop) so lowest spatie/image agrees

Refs #11

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- lang/en.json and lang/dv.json for every string: pages, pickers, editor blocks,
  permission descriptions, crop presets and file types
- Tests: every string has both languages, Dhivehi keeps its placeholders, the
  TypeScript strings match PHP, and views, components and CSS use only logical
  directions

Refs #8

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The earlier edit didn't apply: Prettier had reindented the workflow.

Refs #9, refs #11

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ine and skill

- Docusaurus pages: intro, requirements, installation, upgrading from the
  unreleased main, the library, picker fields, attachments, conversions, cropping,
  block editor blocks, configuration, JSON API, permissions, overriding,
  translatable, Dhivehi and RTL (with a review list), JavaScript, API reference
- llms.txt with the rules and every page; tests check its links
- resources/boost guideline and the javaabu-mediapicker skill with references
- AGENTS.md and CLAUDE.md; README and CHANGELOG
- React entry and pages import the stylesheet; apps get Onu media's peers from manifest
- mediapicker:clear's --force option works

Closes #6, closes #7

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… in tests

Same safeguard as javaabu/forms (Javaabu/forms#21). Boost leaves a
guideline out of apps' AGENTS.md when Blade can't render it, and its
placeholders cover component tags and backticks but not every directive
or echo. The current guideline already rendered on Boost 2.10.0 and
2.10.4; the verbatim block keeps future examples safe. BoostTest renders
every .blade.php under resources/boost with Boost's RendersBladeGuidelines
(laravel/boost ^2.10 in require-dev, so prefer-lowest tests 2.10.0).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Blade scripts (picker, cropper, blocks) import only cropperjs and
@tiptap/core, so those stay in the assets scope. @javaabu/forms,
@javaabu/onu and the Onu media libraries, with the react, react-dom and
@inertiajs/react peers, move to npm(..., for: 'pages'), so manifest:check
in a Blade-only app no longer asks for React packages
(Javaabu/manifest#17). ManifestCheckTest runs manifest:check against a
Blade-only and an Inertia package.json fixture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Composer read composer.json from every branch and tag of each VCS
repository through the GitHub API: about 350 calls per CI job, around
3,800 per run. A second run within the hour exhausted the workflow
token's limit, and Composer failed with "Could not authenticate against
github.com" (Javaabu/exports#2, Javaabu/translatable#4). "no-api": true
makes Composer clone these repositories instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every job downloaded each package's zipball from api.github.com, around
140 calls a job, which counts against the workflow token's hourly limit.
Each job now restores Composer's files cache, keyed by job, matrix entry
and composer.json/composer.lock, and falls back to the job's last cache
when those change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant