@jhb.software/payload-alt-text-plugin 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,6 +54,8 @@ export default buildConfig({
54
54
 
55
55
  Note: When localization is disabled in your Payload config (default), you need to specify the locale to generate the alt texts in via the `locale` plugin option.
56
56
 
57
+ To restrict which MIME types the plugin tracks, validates, and generates for — or to override the default validator on a per-collection basis — pass an object instead of a bare slug. See [Per-collection options](#per-collection-options).
58
+
57
59
  ### Admin list search
58
60
 
59
61
  By default, the plugin sets `admin.listSearchableFields` on the configured upload collections to `['filename', 'keywords', 'alt']` so the admin list-view search matches against these fields. To opt out, set `admin.listSearchableFields` on the collection yourself — any explicit value is preserved as-is:
@@ -75,16 +77,59 @@ This is also the recommended escape hatch if you hit Payload's Postgres SQL-buil
75
77
 
76
78
  ### Plugin Options
77
79
 
78
- | Option | Type | Required | Description |
79
- | ---------------------------- | ------------------ | -------- | ---------------------------------------------------------------------------------------------------------------- |
80
- | `collections` | `CollectionSlug[]` | Yes | Collections to enable alt text generation for |
81
- | `resolver` | `AltTextResolver` | Yes | Alt text resolver to use (e.g., `openAIResolver`) |
82
- | `getImageThumbnail` | `Function` | Yes | Function to get the thumbnail URL from an image document |
83
- | `enabled` | `boolean` | No | Whether to enable the plugin |
84
- | `locale` | `string` | No | Locale for alt text generation (required when localization is disabled) |
85
- | `maxBulkGenerateConcurrency` | `number` | No | Maximum concurrent API requests for bulk operations (default: 16) |
86
- | `fieldsOverride` | `Function` | No | Override the default fields inserted by the plugin |
87
- | `healthCheck` | `boolean` | No | Enable alt text health tracking: REST endpoint, cache revalidation hooks, and dashboard widget (default: `true`) |
80
+ | Option | Type | Required | Description |
81
+ | ---------------------------- | ------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
82
+ | `collections` | `(CollectionSlug \| CollectionObj)[]` | Yes | Collections to enable alt text generation for (see [Per-collection options](#per-collection-options)) |
83
+ | `resolver` | `AltTextResolver` | Yes | Alt text resolver to use (e.g., `openAIResolver`) |
84
+ | `getImageThumbnail` | `Function` | Yes | Function to get the thumbnail URL from an image document |
85
+ | `enabled` | `boolean` | No | Whether to enable the plugin |
86
+ | `locale` | `string` | No | Locale for alt text generation (required when localization is disabled) |
87
+ | `maxBulkGenerateConcurrency` | `number` | No | Maximum concurrent API requests for bulk operations (default: 16) |
88
+ | `fieldsOverride` | `Function` | No | Override the default fields inserted by the plugin |
89
+ | `healthCheck` | `boolean` | No | Enable alt text health tracking: REST endpoint, cache revalidation hooks, and dashboard widget (default: `true`) |
90
+
91
+ ### Per-collection options
92
+
93
+ Each entry in `collections` may be either a bare collection slug (shorthand, defaults to `['image/*']` for `mimeTypes`) or an object with the following fields:
94
+
95
+ | Option | Type | Required | Description |
96
+ | ----------- | ------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
97
+ | `slug` | `CollectionSlug` | Yes | The collection slug |
98
+ | `mimeTypes` | `string[]` | No | MIME types the plugin tracks, validates, and generates for. Supports wildcards like `image/*`. Defaults to `['image/*']`. |
99
+ | `validate` | `TextareaFieldValidation` | No | Custom validator that fully replaces the default required-alt check. Import `validateAltText` from the plugin to compose around the default behavior (see [Custom validator](#custom-validator)). |
100
+
101
+ ```ts
102
+ payloadAltTextPlugin({
103
+ collections: [
104
+ 'images', // shorthand — defaults to mimeTypes: ['image/*']
105
+ { slug: 'media', mimeTypes: ['image/*', 'application/pdf'] },
106
+ ],
107
+ // ...
108
+ })
109
+ ```
110
+
111
+ #### Custom validator
112
+
113
+ The default validator requires alt text on every tracked document. Some workflows — folder moves, partial API updates, or localized setups with `fallback: false` where some locales are intentionally empty — need to skip that check when the request body does not touch `alt`. Pass a `validate` function to override the default, and compose around the exported `validateAltText` to keep the standard behavior for full updates:
114
+
115
+ ```ts
116
+ import { payloadAltTextPlugin, validateAltText } from '@jhb.software/payload-alt-text-plugin'
117
+
118
+ payloadAltTextPlugin({
119
+ collections: [
120
+ {
121
+ slug: 'media',
122
+ validate: (value, args) => {
123
+ // Skip the required-alt check when the request body does not touch `alt`
124
+ // (e.g. folder moves, partial API updates).
125
+ if (!args.req.data || !('alt' in args.req.data)) return true
126
+ return validateAltText(value, args)
127
+ },
128
+ },
129
+ ],
130
+ // ...
131
+ })
132
+ ```
88
133
 
89
134
  ## Dashboard Widget
90
135
 
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Pill } from '@payloadcms/ui/elements/Pill';
3
+ import { formatAdminURL } from 'payload/shared';
3
4
  import { getAltTextHealthWidgetData } from '../utilities/altTextHealth.js';
4
5
  import { getAltTextHealthWidgetDisplayState } from '../utilities/altTextHealthWidgetDisplay.js';
5
6
  import { getCollectionLabel } from '../utilities/getCollectionLabel.js';
@@ -9,6 +10,7 @@ import { ImageIcon } from './icons/ImageIcon.js';
9
10
  export async function AltTextHealthWidget({ req }) {
10
11
  const t = req.t;
11
12
  const { collections, errors, isLocalized, localeCount, totalDocs } = await getAltTextHealthWidgetData(req);
13
+ const adminRoute = req.payload.config.routes.admin;
12
14
  return /*#__PURE__*/ _jsxs("div", {
13
15
  className: "card",
14
16
  style: {
@@ -102,7 +104,10 @@ export async function AltTextHealthWidget({ req }) {
102
104
  },
103
105
  children: [
104
106
  /*#__PURE__*/ _jsx("a", {
105
- href: `${req.payload.config.routes.admin}/collections/${collection.collection}`,
107
+ href: formatAdminURL({
108
+ adminRoute,
109
+ path: `/collections/${collection.collection}`
110
+ }),
106
111
  style: {
107
112
  color: 'var(--theme-text)',
108
113
  fontSize: '14px',
@@ -151,7 +156,10 @@ export async function AltTextHealthWidget({ req }) {
151
156
  displayState === 'unhealthy' && collection.invalidDocIds && collection.invalidDocIds.length > 0 ? /*#__PURE__*/ _jsx(Pill, {
152
157
  pillStyle: "error",
153
158
  size: "small",
154
- to: `${req.payload.config.routes.admin}/collections/${collection.collection}?where[id][in]=${collection.invalidDocIds.join(',')}`,
159
+ to: `${formatAdminURL({
160
+ adminRoute,
161
+ path: `/collections/${collection.collection}`
162
+ })}?where[id][in]=${collection.invalidDocIds.join(',')}`,
155
163
  children: /*#__PURE__*/ _jsxs("div", {
156
164
  style: {
157
165
  alignItems: 'center',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/AltTextHealthWidget.tsx"],"sourcesContent":["import type { TFunction } from '@payloadcms/translations'\nimport type { WidgetServerProps } from 'payload'\n\nimport { Pill } from '@payloadcms/ui/elements/Pill'\n\nimport type { PluginAltTextTranslationKeys } from '../translations/index.js'\n\nimport { getAltTextHealthWidgetData } from '../utilities/altTextHealth.js'\nimport { getAltTextHealthWidgetDisplayState } from '../utilities/altTextHealthWidgetDisplay.js'\nimport { getCollectionLabel } from '../utilities/getCollectionLabel.js'\nimport { ArrowRightIcon } from './icons/ArrowRightIcon.js'\nimport { CheckIcon } from './icons/CheckIcon.js'\nimport { ImageIcon } from './icons/ImageIcon.js'\n\nexport async function AltTextHealthWidget({ req }: WidgetServerProps) {\n const t = req.t as TFunction<PluginAltTextTranslationKeys>\n const { collections, errors, isLocalized, localeCount, totalDocs } =\n await getAltTextHealthWidgetData(req)\n\n return (\n <div\n className=\"card\"\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '16px',\n height: '100%',\n }}\n >\n <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>\n <div style={{ alignItems: 'center', display: 'flex', gap: '0.5rem' }}>\n <div style={{ color: 'var(--theme-elevation-500)' }}>\n <ImageIcon />\n </div>\n <h3 style={{ margin: 0 }}>\n {t('@jhb.software/payload-alt-text-plugin:altTextHealthWidget')}\n </h3>\n </div>\n <p style={{ color: 'var(--theme-text)', fontSize: '14px', margin: 0, opacity: 0.75 }}>\n {t('@jhb.software/payload-alt-text-plugin:altTextHealthDescription')}\n </p>\n </div>\n\n {totalDocs === 0 && errors.length === 0 && (\n <p style={{ color: 'var(--theme-text)', margin: 0, opacity: 0.75 }}>\n {t('@jhb.software/payload-alt-text-plugin:noImagesFound')}\n </p>\n )}\n\n {errors.length > 0 && (\n <p style={{ color: '#92400e', fontSize: '13px', margin: 0 }}>\n {t('@jhb.software/payload-alt-text-plugin:healthCheckPartialWarning')}\n </p>\n )}\n\n <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>\n {collections.map((collection) => {\n const displayState = getAltTextHealthWidgetDisplayState(collection)\n\n return (\n <div\n key={collection.collection}\n style={{\n alignItems: 'center',\n background: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-border-color)',\n borderRadius: 'var(--style-radius-m)',\n display: 'flex',\n gap: '12px',\n justifyContent: 'space-between',\n padding: '12px 16px',\n }}\n >\n <div style={{ display: 'flex', flexDirection: 'column', gap: '2px', minWidth: 0 }}>\n <a\n href={`${req.payload.config.routes.admin}/collections/${collection.collection}`}\n style={{\n color: 'var(--theme-text)',\n fontSize: '14px',\n fontWeight: 500,\n textDecoration: 'none',\n }}\n >\n {getCollectionLabel(\n collection.collection,\n req.payload.config.collections,\n req.locale,\n )}\n </a>\n\n {displayState === 'unavailable' ? (\n <span style={{ color: '#92400e', fontSize: '13px' }}>\n {t('@jhb.software/payload-alt-text-plugin:collectionCheckFailed')}\n </span>\n ) : (\n <span style={{ fontSize: '13px', opacity: 0.7 }}>\n <span style={{ whiteSpace: 'nowrap' }}>\n {t('@jhb.software/payload-alt-text-plugin:totalImageCount', {\n count: collection.totalDocs,\n })}\n </span>\n {isLocalized && (\n <>\n {' · '}\n <span style={{ whiteSpace: 'nowrap' }}>\n {t('@jhb.software/payload-alt-text-plugin:localeCount', {\n count: localeCount,\n })}\n </span>\n </>\n )}\n </span>\n )}\n </div>\n\n {displayState === 'unhealthy' &&\n collection.invalidDocIds &&\n collection.invalidDocIds.length > 0 ? (\n <Pill\n pillStyle=\"error\"\n size=\"small\"\n to={`${req.payload.config.routes.admin}/collections/${collection.collection}?where[id][in]=${collection.invalidDocIds.join(',')}`}\n >\n <div style={{ alignItems: 'center', display: 'flex', gap: '0.25rem' }}>\n {t('@jhb.software/payload-alt-text-plugin:statusUnhealthy', {\n count: collection.missingDocs + collection.partialDocs,\n })}\n <ArrowRightIcon height=\"12\" width=\"12\" />\n </div>\n </Pill>\n ) : displayState === 'healthy' ? (\n <Pill pillStyle=\"success\" size=\"small\">\n <div style={{ alignItems: 'center', display: 'flex', gap: '0.25rem' }}>\n {t('@jhb.software/payload-alt-text-plugin:statusHealthy')}\n <CheckIcon height=\"12\" width=\"12\" />\n </div>\n </Pill>\n ) : displayState === 'unhealthy' ? (\n <Pill pillStyle=\"error\" size=\"small\">\n {t('@jhb.software/payload-alt-text-plugin:statusUnhealthy', {\n count: collection.missingDocs + collection.partialDocs,\n })}\n </Pill>\n ) : (\n <Pill pillStyle=\"warning\" size=\"small\">\n {t('@jhb.software/payload-alt-text-plugin:collectionCheckFailed')}\n </Pill>\n )}\n </div>\n )\n })}\n </div>\n </div>\n )\n}\n"],"names":["Pill","getAltTextHealthWidgetData","getAltTextHealthWidgetDisplayState","getCollectionLabel","ArrowRightIcon","CheckIcon","ImageIcon","AltTextHealthWidget","req","t","collections","errors","isLocalized","localeCount","totalDocs","div","className","style","display","flexDirection","gap","height","alignItems","color","h3","margin","p","fontSize","opacity","length","map","collection","displayState","background","border","borderRadius","justifyContent","padding","minWidth","a","href","payload","config","routes","admin","fontWeight","textDecoration","locale","span","whiteSpace","count","invalidDocIds","pillStyle","size","to","join","missingDocs","partialDocs","width"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/AltTextHealthWidget.tsx"],"sourcesContent":["import type { TFunction } from '@payloadcms/translations'\nimport type { WidgetServerProps } from 'payload'\n\nimport { Pill } from '@payloadcms/ui/elements/Pill'\nimport { formatAdminURL } from 'payload/shared'\n\nimport type { PluginAltTextTranslationKeys } from '../translations/index.js'\n\nimport { getAltTextHealthWidgetData } from '../utilities/altTextHealth.js'\nimport { getAltTextHealthWidgetDisplayState } from '../utilities/altTextHealthWidgetDisplay.js'\nimport { getCollectionLabel } from '../utilities/getCollectionLabel.js'\nimport { ArrowRightIcon } from './icons/ArrowRightIcon.js'\nimport { CheckIcon } from './icons/CheckIcon.js'\nimport { ImageIcon } from './icons/ImageIcon.js'\n\nexport async function AltTextHealthWidget({ req }: WidgetServerProps) {\n const t = req.t as TFunction<PluginAltTextTranslationKeys>\n const { collections, errors, isLocalized, localeCount, totalDocs } =\n await getAltTextHealthWidgetData(req)\n const adminRoute = req.payload.config.routes.admin\n\n return (\n <div\n className=\"card\"\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '16px',\n height: '100%',\n }}\n >\n <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>\n <div style={{ alignItems: 'center', display: 'flex', gap: '0.5rem' }}>\n <div style={{ color: 'var(--theme-elevation-500)' }}>\n <ImageIcon />\n </div>\n <h3 style={{ margin: 0 }}>\n {t('@jhb.software/payload-alt-text-plugin:altTextHealthWidget')}\n </h3>\n </div>\n <p style={{ color: 'var(--theme-text)', fontSize: '14px', margin: 0, opacity: 0.75 }}>\n {t('@jhb.software/payload-alt-text-plugin:altTextHealthDescription')}\n </p>\n </div>\n\n {totalDocs === 0 && errors.length === 0 && (\n <p style={{ color: 'var(--theme-text)', margin: 0, opacity: 0.75 }}>\n {t('@jhb.software/payload-alt-text-plugin:noImagesFound')}\n </p>\n )}\n\n {errors.length > 0 && (\n <p style={{ color: '#92400e', fontSize: '13px', margin: 0 }}>\n {t('@jhb.software/payload-alt-text-plugin:healthCheckPartialWarning')}\n </p>\n )}\n\n <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>\n {collections.map((collection) => {\n const displayState = getAltTextHealthWidgetDisplayState(collection)\n\n return (\n <div\n key={collection.collection}\n style={{\n alignItems: 'center',\n background: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-border-color)',\n borderRadius: 'var(--style-radius-m)',\n display: 'flex',\n gap: '12px',\n justifyContent: 'space-between',\n padding: '12px 16px',\n }}\n >\n <div style={{ display: 'flex', flexDirection: 'column', gap: '2px', minWidth: 0 }}>\n <a\n href={formatAdminURL({\n adminRoute,\n path: `/collections/${collection.collection}`,\n })}\n style={{\n color: 'var(--theme-text)',\n fontSize: '14px',\n fontWeight: 500,\n textDecoration: 'none',\n }}\n >\n {getCollectionLabel(\n collection.collection,\n req.payload.config.collections,\n req.locale,\n )}\n </a>\n\n {displayState === 'unavailable' ? (\n <span style={{ color: '#92400e', fontSize: '13px' }}>\n {t('@jhb.software/payload-alt-text-plugin:collectionCheckFailed')}\n </span>\n ) : (\n <span style={{ fontSize: '13px', opacity: 0.7 }}>\n <span style={{ whiteSpace: 'nowrap' }}>\n {t('@jhb.software/payload-alt-text-plugin:totalImageCount', {\n count: collection.totalDocs,\n })}\n </span>\n {isLocalized && (\n <>\n {' · '}\n <span style={{ whiteSpace: 'nowrap' }}>\n {t('@jhb.software/payload-alt-text-plugin:localeCount', {\n count: localeCount,\n })}\n </span>\n </>\n )}\n </span>\n )}\n </div>\n\n {displayState === 'unhealthy' &&\n collection.invalidDocIds &&\n collection.invalidDocIds.length > 0 ? (\n <Pill\n pillStyle=\"error\"\n size=\"small\"\n to={`${formatAdminURL({\n adminRoute,\n path: `/collections/${collection.collection}`,\n })}?where[id][in]=${collection.invalidDocIds.join(',')}`}\n >\n <div style={{ alignItems: 'center', display: 'flex', gap: '0.25rem' }}>\n {t('@jhb.software/payload-alt-text-plugin:statusUnhealthy', {\n count: collection.missingDocs + collection.partialDocs,\n })}\n <ArrowRightIcon height=\"12\" width=\"12\" />\n </div>\n </Pill>\n ) : displayState === 'healthy' ? (\n <Pill pillStyle=\"success\" size=\"small\">\n <div style={{ alignItems: 'center', display: 'flex', gap: '0.25rem' }}>\n {t('@jhb.software/payload-alt-text-plugin:statusHealthy')}\n <CheckIcon height=\"12\" width=\"12\" />\n </div>\n </Pill>\n ) : displayState === 'unhealthy' ? (\n <Pill pillStyle=\"error\" size=\"small\">\n {t('@jhb.software/payload-alt-text-plugin:statusUnhealthy', {\n count: collection.missingDocs + collection.partialDocs,\n })}\n </Pill>\n ) : (\n <Pill pillStyle=\"warning\" size=\"small\">\n {t('@jhb.software/payload-alt-text-plugin:collectionCheckFailed')}\n </Pill>\n )}\n </div>\n )\n })}\n </div>\n </div>\n )\n}\n"],"names":["Pill","formatAdminURL","getAltTextHealthWidgetData","getAltTextHealthWidgetDisplayState","getCollectionLabel","ArrowRightIcon","CheckIcon","ImageIcon","AltTextHealthWidget","req","t","collections","errors","isLocalized","localeCount","totalDocs","adminRoute","payload","config","routes","admin","div","className","style","display","flexDirection","gap","height","alignItems","color","h3","margin","p","fontSize","opacity","length","map","collection","displayState","background","border","borderRadius","justifyContent","padding","minWidth","a","href","path","fontWeight","textDecoration","locale","span","whiteSpace","count","invalidDocIds","pillStyle","size","to","join","missingDocs","partialDocs","width"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { Button, toast, useSelection, useTranslation } from '@payloadcms/ui';
3
+ import { Button, toast, useConfig, useSelection, useTranslation } from '@payloadcms/ui';
4
4
  import { useRouter } from 'next/navigation.js';
5
5
  import { useTransition } from 'react';
6
6
  import { Lightning } from './icons/Lightning.js';
@@ -9,6 +9,7 @@ export function BulkGenerateAltTextsButton({ collectionSlug }) {
9
9
  const { t } = useTranslation();
10
10
  const [isPending, startTransition] = useTransition();
11
11
  const { selected, setSelection } = useSelection();
12
+ const { config: { routes: { api: apiRoute }, serverURL } } = useConfig();
12
13
  const selectedIds = Array.from(selected.entries()).filter(([, isSelected])=>isSelected).map(([id])=>id);
13
14
  const router = useRouter();
14
15
  const handleGenerateAltTexts = ()=>{
@@ -17,7 +18,7 @@ export function BulkGenerateAltTextsButton({ collectionSlug }) {
17
18
  throw new Error('Collection slug is required');
18
19
  }
19
20
  try {
20
- const response = await fetch('/api/alt-text-plugin/generate/bulk', {
21
+ const response = await fetch(`${serverURL ?? ''}${apiRoute}/alt-text-plugin/generate/bulk`, {
21
22
  body: JSON.stringify({
22
23
  collection: collectionSlug,
23
24
  ids: selectedIds
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/BulkGenerateAltTextsButton.tsx"],"sourcesContent":["'use client'\n\nimport { Button, toast, useSelection, useTranslation } from '@payloadcms/ui'\nimport { useRouter } from 'next/navigation.js'\nimport { useTransition } from 'react'\n\nimport type {\n PluginAltTextTranslationKeys,\n PluginAltTextTranslations,\n} from '../translations/index.js'\n\nimport { Lightning } from './icons/Lightning.js'\nimport { Spinner } from './icons/Spinner.js'\n\nexport function BulkGenerateAltTextsButton({ collectionSlug }: { collectionSlug: string }) {\n const { t } = useTranslation<PluginAltTextTranslations, PluginAltTextTranslationKeys>()\n const [isPending, startTransition] = useTransition()\n const { selected, setSelection } = useSelection()\n\n const selectedIds = Array.from(selected.entries())\n .filter(([, isSelected]) => isSelected)\n .map(([id]) => id) as string[]\n\n const router = useRouter()\n\n const handleGenerateAltTexts = () => {\n startTransition(async () => {\n if (!collectionSlug) {\n throw new Error('Collection slug is required')\n }\n\n try {\n const response = await fetch('/api/alt-text-plugin/generate/bulk', {\n body: JSON.stringify({\n collection: collectionSlug,\n ids: selectedIds,\n }),\n method: 'POST',\n })\n\n if (!response.ok) {\n toast.error(t('@jhb.software/payload-alt-text-plugin:failedToGenerate'))\n return\n }\n\n const data = (await response.json()) as {\n erroredDocs: string[]\n totalDocs: number\n updatedDocs: number\n }\n\n if (data.erroredDocs.length > 0) {\n toast.error(\n t('@jhb.software/payload-alt-text-plugin:failedToGenerateForXImages', {\n count: data.erroredDocs.length,\n }),\n )\n }\n\n // in case not all images were updated, show a warning instead of a success message:\n if (data.updatedDocs === data.totalDocs) {\n toast.success(\n t('@jhb.software/payload-alt-text-plugin:xOfYImagesUpdated', {\n total: data.totalDocs,\n updated: data.updatedDocs,\n }),\n )\n } else {\n toast.warning(\n t('@jhb.software/payload-alt-text-plugin:xOfYImagesUpdated', {\n total: data.totalDocs,\n updated: data.updatedDocs,\n }),\n )\n }\n\n // deselect all previously selected images\n for (const id of selectedIds) {\n setSelection(id)\n }\n\n router.refresh()\n } catch (error) {\n console.error('Error generating alt text:', error)\n toast.error(t('@jhb.software/payload-alt-text-plugin:errorGeneratingAltText'))\n }\n })\n }\n\n return (\n selectedIds.length > 0 && (\n <div className=\"m-0\" style={{ display: 'flex', justifyContent: 'right' }}>\n <Button\n className=\"m-0\"\n disabled={isPending || selectedIds.length === 0}\n icon={isPending ? <Spinner /> : <Lightning />}\n onClick={handleGenerateAltTexts}\n >\n {t('@jhb.software/payload-alt-text-plugin:generateAltTextFor', {\n count: selectedIds.length,\n })}\n </Button>\n </div>\n )\n )\n}\n"],"names":["Button","toast","useSelection","useTranslation","useRouter","useTransition","Lightning","Spinner","BulkGenerateAltTextsButton","collectionSlug","t","isPending","startTransition","selected","setSelection","selectedIds","Array","from","entries","filter","isSelected","map","id","router","handleGenerateAltTexts","Error","response","fetch","body","JSON","stringify","collection","ids","method","ok","error","data","json","erroredDocs","length","count","updatedDocs","totalDocs","success","total","updated","warning","refresh","console","div","className","style","display","justifyContent","disabled","icon","onClick"],"mappings":"AAAA;;AAEA,SAASA,MAAM,EAAEC,KAAK,EAAEC,YAAY,EAAEC,cAAc,QAAQ,iBAAgB;AAC5E,SAASC,SAAS,QAAQ,qBAAoB;AAC9C,SAASC,aAAa,QAAQ,QAAO;AAOrC,SAASC,SAAS,QAAQ,uBAAsB;AAChD,SAASC,OAAO,QAAQ,qBAAoB;AAE5C,OAAO,SAASC,2BAA2B,EAAEC,cAAc,EAA8B;IACvF,MAAM,EAAEC,CAAC,EAAE,GAAGP;IACd,MAAM,CAACQ,WAAWC,gBAAgB,GAAGP;IACrC,MAAM,EAAEQ,QAAQ,EAAEC,YAAY,EAAE,GAAGZ;IAEnC,MAAMa,cAAcC,MAAMC,IAAI,CAACJ,SAASK,OAAO,IAC5CC,MAAM,CAAC,CAAC,GAAGC,WAAW,GAAKA,YAC3BC,GAAG,CAAC,CAAC,CAACC,GAAG,GAAKA;IAEjB,MAAMC,SAASnB;IAEf,MAAMoB,yBAAyB;QAC7BZ,gBAAgB;YACd,IAAI,CAACH,gBAAgB;gBACnB,MAAM,IAAIgB,MAAM;YAClB;YAEA,IAAI;gBACF,MAAMC,WAAW,MAAMC,MAAM,sCAAsC;oBACjEC,MAAMC,KAAKC,SAAS,CAAC;wBACnBC,YAAYtB;wBACZuB,KAAKjB;oBACP;oBACAkB,QAAQ;gBACV;gBAEA,IAAI,CAACP,SAASQ,EAAE,EAAE;oBAChBjC,MAAMkC,KAAK,CAACzB,EAAE;oBACd;gBACF;gBAEA,MAAM0B,OAAQ,MAAMV,SAASW,IAAI;gBAMjC,IAAID,KAAKE,WAAW,CAACC,MAAM,GAAG,GAAG;oBAC/BtC,MAAMkC,KAAK,CACTzB,EAAE,oEAAoE;wBACpE8B,OAAOJ,KAAKE,WAAW,CAACC,MAAM;oBAChC;gBAEJ;gBAEA,oFAAoF;gBACpF,IAAIH,KAAKK,WAAW,KAAKL,KAAKM,SAAS,EAAE;oBACvCzC,MAAM0C,OAAO,CACXjC,EAAE,2DAA2D;wBAC3DkC,OAAOR,KAAKM,SAAS;wBACrBG,SAAST,KAAKK,WAAW;oBAC3B;gBAEJ,OAAO;oBACLxC,MAAM6C,OAAO,CACXpC,EAAE,2DAA2D;wBAC3DkC,OAAOR,KAAKM,SAAS;wBACrBG,SAAST,KAAKK,WAAW;oBAC3B;gBAEJ;gBAEA,0CAA0C;gBAC1C,KAAK,MAAMnB,MAAMP,YAAa;oBAC5BD,aAAaQ;gBACf;gBAEAC,OAAOwB,OAAO;YAChB,EAAE,OAAOZ,OAAO;gBACda,QAAQb,KAAK,CAAC,8BAA8BA;gBAC5ClC,MAAMkC,KAAK,CAACzB,EAAE;YAChB;QACF;IACF;IAEA,OACEK,YAAYwB,MAAM,GAAG,mBACnB,KAACU;QAAIC,WAAU;QAAMC,OAAO;YAAEC,SAAS;YAAQC,gBAAgB;QAAQ;kBACrE,cAAA,KAACrD;YACCkD,WAAU;YACVI,UAAU3C,aAAaI,YAAYwB,MAAM,KAAK;YAC9CgB,MAAM5C,0BAAY,KAACJ,6BAAa,KAACD;YACjCkD,SAAShC;sBAERd,EAAE,4DAA4D;gBAC7D8B,OAAOzB,YAAYwB,MAAM;YAC3B;;;AAKV"}
1
+ {"version":3,"sources":["../../src/components/BulkGenerateAltTextsButton.tsx"],"sourcesContent":["'use client'\n\nimport { Button, toast, useConfig, useSelection, useTranslation } from '@payloadcms/ui'\nimport { useRouter } from 'next/navigation.js'\nimport { useTransition } from 'react'\n\nimport type {\n PluginAltTextTranslationKeys,\n PluginAltTextTranslations,\n} from '../translations/index.js'\n\nimport { Lightning } from './icons/Lightning.js'\nimport { Spinner } from './icons/Spinner.js'\n\nexport function BulkGenerateAltTextsButton({ collectionSlug }: { collectionSlug: string }) {\n const { t } = useTranslation<PluginAltTextTranslations, PluginAltTextTranslationKeys>()\n const [isPending, startTransition] = useTransition()\n const { selected, setSelection } = useSelection()\n const {\n config: {\n routes: { api: apiRoute },\n serverURL,\n },\n } = useConfig()\n\n const selectedIds = Array.from(selected.entries())\n .filter(([, isSelected]) => isSelected)\n .map(([id]) => id) as string[]\n\n const router = useRouter()\n\n const handleGenerateAltTexts = () => {\n startTransition(async () => {\n if (!collectionSlug) {\n throw new Error('Collection slug is required')\n }\n\n try {\n const response = await fetch(\n `${serverURL ?? ''}${apiRoute}/alt-text-plugin/generate/bulk`,\n {\n body: JSON.stringify({\n collection: collectionSlug,\n ids: selectedIds,\n }),\n method: 'POST',\n },\n )\n\n if (!response.ok) {\n toast.error(t('@jhb.software/payload-alt-text-plugin:failedToGenerate'))\n return\n }\n\n const data = (await response.json()) as {\n erroredDocs: string[]\n totalDocs: number\n updatedDocs: number\n }\n\n if (data.erroredDocs.length > 0) {\n toast.error(\n t('@jhb.software/payload-alt-text-plugin:failedToGenerateForXImages', {\n count: data.erroredDocs.length,\n }),\n )\n }\n\n // in case not all images were updated, show a warning instead of a success message:\n if (data.updatedDocs === data.totalDocs) {\n toast.success(\n t('@jhb.software/payload-alt-text-plugin:xOfYImagesUpdated', {\n total: data.totalDocs,\n updated: data.updatedDocs,\n }),\n )\n } else {\n toast.warning(\n t('@jhb.software/payload-alt-text-plugin:xOfYImagesUpdated', {\n total: data.totalDocs,\n updated: data.updatedDocs,\n }),\n )\n }\n\n // deselect all previously selected images\n for (const id of selectedIds) {\n setSelection(id)\n }\n\n router.refresh()\n } catch (error) {\n console.error('Error generating alt text:', error)\n toast.error(t('@jhb.software/payload-alt-text-plugin:errorGeneratingAltText'))\n }\n })\n }\n\n return (\n selectedIds.length > 0 && (\n <div className=\"m-0\" style={{ display: 'flex', justifyContent: 'right' }}>\n <Button\n className=\"m-0\"\n disabled={isPending || selectedIds.length === 0}\n icon={isPending ? <Spinner /> : <Lightning />}\n onClick={handleGenerateAltTexts}\n >\n {t('@jhb.software/payload-alt-text-plugin:generateAltTextFor', {\n count: selectedIds.length,\n })}\n </Button>\n </div>\n )\n )\n}\n"],"names":["Button","toast","useConfig","useSelection","useTranslation","useRouter","useTransition","Lightning","Spinner","BulkGenerateAltTextsButton","collectionSlug","t","isPending","startTransition","selected","setSelection","config","routes","api","apiRoute","serverURL","selectedIds","Array","from","entries","filter","isSelected","map","id","router","handleGenerateAltTexts","Error","response","fetch","body","JSON","stringify","collection","ids","method","ok","error","data","json","erroredDocs","length","count","updatedDocs","totalDocs","success","total","updated","warning","refresh","console","div","className","style","display","justifyContent","disabled","icon","onClick"],"mappings":"AAAA;;AAEA,SAASA,MAAM,EAAEC,KAAK,EAAEC,SAAS,EAAEC,YAAY,EAAEC,cAAc,QAAQ,iBAAgB;AACvF,SAASC,SAAS,QAAQ,qBAAoB;AAC9C,SAASC,aAAa,QAAQ,QAAO;AAOrC,SAASC,SAAS,QAAQ,uBAAsB;AAChD,SAASC,OAAO,QAAQ,qBAAoB;AAE5C,OAAO,SAASC,2BAA2B,EAAEC,cAAc,EAA8B;IACvF,MAAM,EAAEC,CAAC,EAAE,GAAGP;IACd,MAAM,CAACQ,WAAWC,gBAAgB,GAAGP;IACrC,MAAM,EAAEQ,QAAQ,EAAEC,YAAY,EAAE,GAAGZ;IACnC,MAAM,EACJa,QAAQ,EACNC,QAAQ,EAAEC,KAAKC,QAAQ,EAAE,EACzBC,SAAS,EACV,EACF,GAAGlB;IAEJ,MAAMmB,cAAcC,MAAMC,IAAI,CAACT,SAASU,OAAO,IAC5CC,MAAM,CAAC,CAAC,GAAGC,WAAW,GAAKA,YAC3BC,GAAG,CAAC,CAAC,CAACC,GAAG,GAAKA;IAEjB,MAAMC,SAASxB;IAEf,MAAMyB,yBAAyB;QAC7BjB,gBAAgB;YACd,IAAI,CAACH,gBAAgB;gBACnB,MAAM,IAAIqB,MAAM;YAClB;YAEA,IAAI;gBACF,MAAMC,WAAW,MAAMC,MACrB,GAAGb,aAAa,KAAKD,SAAS,8BAA8B,CAAC,EAC7D;oBACEe,MAAMC,KAAKC,SAAS,CAAC;wBACnBC,YAAY3B;wBACZ4B,KAAKjB;oBACP;oBACAkB,QAAQ;gBACV;gBAGF,IAAI,CAACP,SAASQ,EAAE,EAAE;oBAChBvC,MAAMwC,KAAK,CAAC9B,EAAE;oBACd;gBACF;gBAEA,MAAM+B,OAAQ,MAAMV,SAASW,IAAI;gBAMjC,IAAID,KAAKE,WAAW,CAACC,MAAM,GAAG,GAAG;oBAC/B5C,MAAMwC,KAAK,CACT9B,EAAE,oEAAoE;wBACpEmC,OAAOJ,KAAKE,WAAW,CAACC,MAAM;oBAChC;gBAEJ;gBAEA,oFAAoF;gBACpF,IAAIH,KAAKK,WAAW,KAAKL,KAAKM,SAAS,EAAE;oBACvC/C,MAAMgD,OAAO,CACXtC,EAAE,2DAA2D;wBAC3DuC,OAAOR,KAAKM,SAAS;wBACrBG,SAAST,KAAKK,WAAW;oBAC3B;gBAEJ,OAAO;oBACL9C,MAAMmD,OAAO,CACXzC,EAAE,2DAA2D;wBAC3DuC,OAAOR,KAAKM,SAAS;wBACrBG,SAAST,KAAKK,WAAW;oBAC3B;gBAEJ;gBAEA,0CAA0C;gBAC1C,KAAK,MAAMnB,MAAMP,YAAa;oBAC5BN,aAAaa;gBACf;gBAEAC,OAAOwB,OAAO;YAChB,EAAE,OAAOZ,OAAO;gBACda,QAAQb,KAAK,CAAC,8BAA8BA;gBAC5CxC,MAAMwC,KAAK,CAAC9B,EAAE;YAChB;QACF;IACF;IAEA,OACEU,YAAYwB,MAAM,GAAG,mBACnB,KAACU;QAAIC,WAAU;QAAMC,OAAO;YAAEC,SAAS;YAAQC,gBAAgB;QAAQ;kBACrE,cAAA,KAAC3D;YACCwD,WAAU;YACVI,UAAUhD,aAAaS,YAAYwB,MAAM,KAAK;YAC9CgB,MAAMjD,0BAAY,KAACJ,6BAAa,KAACD;YACjCuD,SAAShC;sBAERnB,EAAE,4DAA4D;gBAC7DmC,OAAOzB,YAAYwB,MAAM;YAC3B;;;AAKV"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Button, toast, useDocumentInfo, useField, useLocale, useTranslation } from '@payloadcms/ui';
3
+ import { Button, toast, useConfig, useDocumentInfo, useField, useLocale, useTranslation } from '@payloadcms/ui';
4
4
  import { useTransition } from 'react';
5
5
  import { Lightning } from './icons/Lightning.js';
6
6
  import { Spinner } from './icons/Spinner.js';
@@ -9,6 +9,7 @@ export function GenerateAltTextButton({ supportedMimeTypes }) {
9
9
  const { id, collectionSlug } = useDocumentInfo();
10
10
  const locale = useLocale();
11
11
  const [isPending, startTransition] = useTransition();
12
+ const { config: { routes: { api: apiRoute }, serverURL } } = useConfig();
12
13
  const { setValue: setKeywords } = useField({
13
14
  path: 'keywords'
14
15
  });
@@ -26,7 +27,7 @@ export function GenerateAltTextButton({ supportedMimeTypes }) {
26
27
  }
27
28
  startTransition(async ()=>{
28
29
  try {
29
- const response = await fetch('/api/alt-text-plugin/generate', {
30
+ const response = await fetch(`${serverURL ?? ''}${apiRoute}/alt-text-plugin/generate`, {
30
31
  body: JSON.stringify({
31
32
  id: id,
32
33
  collection: collectionSlug,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/GenerateAltTextButton.tsx"],"sourcesContent":["'use client'\n\nimport { Button, toast, useDocumentInfo, useField, useLocale, useTranslation } from '@payloadcms/ui'\nimport { useTransition } from 'react'\n\nimport type {\n PluginAltTextTranslationKeys,\n PluginAltTextTranslations,\n} from '../translations/index.js'\n\nimport { Lightning } from './icons/Lightning.js'\nimport { Spinner } from './icons/Spinner.js'\n\nexport function GenerateAltTextButton({ supportedMimeTypes }: { supportedMimeTypes?: string[] }) {\n const { t } = useTranslation<PluginAltTextTranslations, PluginAltTextTranslationKeys>()\n const { id, collectionSlug } = useDocumentInfo()\n const locale = useLocale()\n const [isPending, startTransition] = useTransition()\n\n const { setValue: setKeywords } = useField<string>({ path: 'keywords' })\n const { setValue: setAltText } = useField<string>({ path: 'alt' })\n const { value: mimeType } = useField<string>({ path: 'mimeType' })\n\n const isUnsupportedMimeType =\n !!mimeType && !!supportedMimeTypes && !supportedMimeTypes.includes(mimeType)\n\n const handleGenerateAltText = () => {\n if (!collectionSlug || !id) {\n toast.error(t('@jhb.software/payload-alt-text-plugin:cannotGenerateMissingFields'))\n throw new Error('Missing required fields')\n }\n\n startTransition(async () => {\n try {\n const response = await fetch('/api/alt-text-plugin/generate', {\n body: JSON.stringify({\n id: id as string,\n collection: collectionSlug,\n locale: locale?.code ?? null, // sent null when localization is disabled\n }),\n method: 'POST',\n })\n\n if (!response.ok) {\n let errorMessage = t('@jhb.software/payload-alt-text-plugin:failedToGenerate')\n try {\n const errorData = (await response.json()) as { error: string }\n errorMessage = errorData.error\n } catch (error) {\n console.error('Error generating alt text:', error)\n }\n\n toast.error(errorMessage)\n return\n }\n\n const data = (await response.json()) as {\n altText: string\n keywords: string[]\n }\n\n if (data.altText && data.keywords) {\n setAltText(data.altText)\n setKeywords(data.keywords)\n toast.success(t('@jhb.software/payload-alt-text-plugin:altTextGeneratedSuccess'))\n } else {\n toast.error(t('@jhb.software/payload-alt-text-plugin:noAltTextGenerated'))\n }\n } catch (error) {\n console.error('Error generating alt text:', error)\n toast.error(t('@jhb.software/payload-alt-text-plugin:errorGeneratingAltText'))\n }\n })\n }\n\n return (\n <div style={{ display: 'flex', gap: '20px', marginTop: '10px' }}>\n <div style={{ color: 'var(--theme-elevation-400)', flex: '1' }}>\n <p>{t('@jhb.software/payload-alt-text-plugin:altTextDescription')}</p>\n <ol style={{ margin: '10px 0', paddingLeft: '20px' }}>\n <li>{t('@jhb.software/payload-alt-text-plugin:altTextRequirement1')}</li>\n <li>{t('@jhb.software/payload-alt-text-plugin:altTextRequirement2')}</li>\n <li>{t('@jhb.software/payload-alt-text-plugin:altTextRequirement3')}</li>\n </ol>\n </div>\n <div style={{ alignItems: 'center', display: 'flex' }}>\n <Button\n disabled={isPending || !id || isUnsupportedMimeType}\n icon={isPending ? <Spinner /> : <Lightning />}\n onClick={handleGenerateAltText}\n tooltip={\n isUnsupportedMimeType\n ? t('@jhb.software/payload-alt-text-plugin:unsupportedMimeType', { mimeType })\n : !id\n ? t('@jhb.software/payload-alt-text-plugin:pleaseSaveDocumentFirst')\n : undefined\n }\n >\n {t('@jhb.software/payload-alt-text-plugin:generateAltText')}\n </Button>\n </div>\n </div>\n )\n}\n"],"names":["Button","toast","useDocumentInfo","useField","useLocale","useTranslation","useTransition","Lightning","Spinner","GenerateAltTextButton","supportedMimeTypes","t","id","collectionSlug","locale","isPending","startTransition","setValue","setKeywords","path","setAltText","value","mimeType","isUnsupportedMimeType","includes","handleGenerateAltText","error","Error","response","fetch","body","JSON","stringify","collection","code","method","ok","errorMessage","errorData","json","console","data","altText","keywords","success","div","style","display","gap","marginTop","color","flex","p","ol","margin","paddingLeft","li","alignItems","disabled","icon","onClick","tooltip","undefined"],"mappings":"AAAA;;AAEA,SAASA,MAAM,EAAEC,KAAK,EAAEC,eAAe,EAAEC,QAAQ,EAAEC,SAAS,EAAEC,cAAc,QAAQ,iBAAgB;AACpG,SAASC,aAAa,QAAQ,QAAO;AAOrC,SAASC,SAAS,QAAQ,uBAAsB;AAChD,SAASC,OAAO,QAAQ,qBAAoB;AAE5C,OAAO,SAASC,sBAAsB,EAAEC,kBAAkB,EAAqC;IAC7F,MAAM,EAAEC,CAAC,EAAE,GAAGN;IACd,MAAM,EAAEO,EAAE,EAAEC,cAAc,EAAE,GAAGX;IAC/B,MAAMY,SAASV;IACf,MAAM,CAACW,WAAWC,gBAAgB,GAAGV;IAErC,MAAM,EAAEW,UAAUC,WAAW,EAAE,GAAGf,SAAiB;QAAEgB,MAAM;IAAW;IACtE,MAAM,EAAEF,UAAUG,UAAU,EAAE,GAAGjB,SAAiB;QAAEgB,MAAM;IAAM;IAChE,MAAM,EAAEE,OAAOC,QAAQ,EAAE,GAAGnB,SAAiB;QAAEgB,MAAM;IAAW;IAEhE,MAAMI,wBACJ,CAAC,CAACD,YAAY,CAAC,CAACZ,sBAAsB,CAACA,mBAAmBc,QAAQ,CAACF;IAErE,MAAMG,wBAAwB;QAC5B,IAAI,CAACZ,kBAAkB,CAACD,IAAI;YAC1BX,MAAMyB,KAAK,CAACf,EAAE;YACd,MAAM,IAAIgB,MAAM;QAClB;QAEAX,gBAAgB;YACd,IAAI;gBACF,MAAMY,WAAW,MAAMC,MAAM,iCAAiC;oBAC5DC,MAAMC,KAAKC,SAAS,CAAC;wBACnBpB,IAAIA;wBACJqB,YAAYpB;wBACZC,QAAQA,QAAQoB,QAAQ;oBAC1B;oBACAC,QAAQ;gBACV;gBAEA,IAAI,CAACP,SAASQ,EAAE,EAAE;oBAChB,IAAIC,eAAe1B,EAAE;oBACrB,IAAI;wBACF,MAAM2B,YAAa,MAAMV,SAASW,IAAI;wBACtCF,eAAeC,UAAUZ,KAAK;oBAChC,EAAE,OAAOA,OAAO;wBACdc,QAAQd,KAAK,CAAC,8BAA8BA;oBAC9C;oBAEAzB,MAAMyB,KAAK,CAACW;oBACZ;gBACF;gBAEA,MAAMI,OAAQ,MAAMb,SAASW,IAAI;gBAKjC,IAAIE,KAAKC,OAAO,IAAID,KAAKE,QAAQ,EAAE;oBACjCvB,WAAWqB,KAAKC,OAAO;oBACvBxB,YAAYuB,KAAKE,QAAQ;oBACzB1C,MAAM2C,OAAO,CAACjC,EAAE;gBAClB,OAAO;oBACLV,MAAMyB,KAAK,CAACf,EAAE;gBAChB;YACF,EAAE,OAAOe,OAAO;gBACdc,QAAQd,KAAK,CAAC,8BAA8BA;gBAC5CzB,MAAMyB,KAAK,CAACf,EAAE;YAChB;QACF;IACF;IAEA,qBACE,MAACkC;QAAIC,OAAO;YAAEC,SAAS;YAAQC,KAAK;YAAQC,WAAW;QAAO;;0BAC5D,MAACJ;gBAAIC,OAAO;oBAAEI,OAAO;oBAA8BC,MAAM;gBAAI;;kCAC3D,KAACC;kCAAGzC,EAAE;;kCACN,MAAC0C;wBAAGP,OAAO;4BAAEQ,QAAQ;4BAAUC,aAAa;wBAAO;;0CACjD,KAACC;0CAAI7C,EAAE;;0CACP,KAAC6C;0CAAI7C,EAAE;;0CACP,KAAC6C;0CAAI7C,EAAE;;;;;;0BAGX,KAACkC;gBAAIC,OAAO;oBAAEW,YAAY;oBAAUV,SAAS;gBAAO;0BAClD,cAAA,KAAC/C;oBACC0D,UAAU3C,aAAa,CAACH,MAAMW;oBAC9BoC,MAAM5C,0BAAY,KAACP,6BAAa,KAACD;oBACjCqD,SAASnC;oBACToC,SACEtC,wBACIZ,EAAE,6DAA6D;wBAAEW;oBAAS,KAC1E,CAACV,KACCD,EAAE,mEACFmD;8BAGPnD,EAAE;;;;;AAKb"}
1
+ {"version":3,"sources":["../../src/components/GenerateAltTextButton.tsx"],"sourcesContent":["'use client'\n\nimport {\n Button,\n toast,\n useConfig,\n useDocumentInfo,\n useField,\n useLocale,\n useTranslation,\n} from '@payloadcms/ui'\nimport { useTransition } from 'react'\n\nimport type {\n PluginAltTextTranslationKeys,\n PluginAltTextTranslations,\n} from '../translations/index.js'\n\nimport { Lightning } from './icons/Lightning.js'\nimport { Spinner } from './icons/Spinner.js'\n\nexport function GenerateAltTextButton({ supportedMimeTypes }: { supportedMimeTypes?: string[] }) {\n const { t } = useTranslation<PluginAltTextTranslations, PluginAltTextTranslationKeys>()\n const { id, collectionSlug } = useDocumentInfo()\n const locale = useLocale()\n const [isPending, startTransition] = useTransition()\n const {\n config: {\n routes: { api: apiRoute },\n serverURL,\n },\n } = useConfig()\n\n const { setValue: setKeywords } = useField<string>({ path: 'keywords' })\n const { setValue: setAltText } = useField<string>({ path: 'alt' })\n const { value: mimeType } = useField<string>({ path: 'mimeType' })\n\n const isUnsupportedMimeType =\n !!mimeType && !!supportedMimeTypes && !supportedMimeTypes.includes(mimeType)\n\n const handleGenerateAltText = () => {\n if (!collectionSlug || !id) {\n toast.error(t('@jhb.software/payload-alt-text-plugin:cannotGenerateMissingFields'))\n throw new Error('Missing required fields')\n }\n\n startTransition(async () => {\n try {\n const response = await fetch(`${serverURL ?? ''}${apiRoute}/alt-text-plugin/generate`, {\n body: JSON.stringify({\n id: id as string,\n collection: collectionSlug,\n locale: locale?.code ?? null, // sent null when localization is disabled\n }),\n method: 'POST',\n })\n\n if (!response.ok) {\n let errorMessage = t('@jhb.software/payload-alt-text-plugin:failedToGenerate')\n try {\n const errorData = (await response.json()) as { error: string }\n errorMessage = errorData.error\n } catch (error) {\n console.error('Error generating alt text:', error)\n }\n\n toast.error(errorMessage)\n return\n }\n\n const data = (await response.json()) as {\n altText: string\n keywords: string[]\n }\n\n if (data.altText && data.keywords) {\n setAltText(data.altText)\n setKeywords(data.keywords)\n toast.success(t('@jhb.software/payload-alt-text-plugin:altTextGeneratedSuccess'))\n } else {\n toast.error(t('@jhb.software/payload-alt-text-plugin:noAltTextGenerated'))\n }\n } catch (error) {\n console.error('Error generating alt text:', error)\n toast.error(t('@jhb.software/payload-alt-text-plugin:errorGeneratingAltText'))\n }\n })\n }\n\n return (\n <div style={{ display: 'flex', gap: '20px', marginTop: '10px' }}>\n <div style={{ color: 'var(--theme-elevation-400)', flex: '1' }}>\n <p>{t('@jhb.software/payload-alt-text-plugin:altTextDescription')}</p>\n <ol style={{ margin: '10px 0', paddingLeft: '20px' }}>\n <li>{t('@jhb.software/payload-alt-text-plugin:altTextRequirement1')}</li>\n <li>{t('@jhb.software/payload-alt-text-plugin:altTextRequirement2')}</li>\n <li>{t('@jhb.software/payload-alt-text-plugin:altTextRequirement3')}</li>\n </ol>\n </div>\n <div style={{ alignItems: 'center', display: 'flex' }}>\n <Button\n disabled={isPending || !id || isUnsupportedMimeType}\n icon={isPending ? <Spinner /> : <Lightning />}\n onClick={handleGenerateAltText}\n tooltip={\n isUnsupportedMimeType\n ? t('@jhb.software/payload-alt-text-plugin:unsupportedMimeType', { mimeType })\n : !id\n ? t('@jhb.software/payload-alt-text-plugin:pleaseSaveDocumentFirst')\n : undefined\n }\n >\n {t('@jhb.software/payload-alt-text-plugin:generateAltText')}\n </Button>\n </div>\n </div>\n )\n}\n"],"names":["Button","toast","useConfig","useDocumentInfo","useField","useLocale","useTranslation","useTransition","Lightning","Spinner","GenerateAltTextButton","supportedMimeTypes","t","id","collectionSlug","locale","isPending","startTransition","config","routes","api","apiRoute","serverURL","setValue","setKeywords","path","setAltText","value","mimeType","isUnsupportedMimeType","includes","handleGenerateAltText","error","Error","response","fetch","body","JSON","stringify","collection","code","method","ok","errorMessage","errorData","json","console","data","altText","keywords","success","div","style","display","gap","marginTop","color","flex","p","ol","margin","paddingLeft","li","alignItems","disabled","icon","onClick","tooltip","undefined"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jhb.software/payload-alt-text-plugin",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A Payload CMS plugin that adds AI-powered alt text generation for images.",
5
5
  "bugs": "https://github.com/jhb-software/payload-plugins/issues",
6
6
  "repository": "https://github.com/jhb-software/payload-plugins",
@@ -17,30 +17,30 @@
17
17
  "main": "./dist/index.js",
18
18
  "types": "./dist/index.d.ts",
19
19
  "dependencies": {
20
- "openai": "^6.34.0",
20
+ "openai": "^6.37.0",
21
21
  "p-map": "^7.0.4",
22
- "zod": "^4.3.6"
22
+ "zod": "^4.4.3"
23
23
  },
24
24
  "peerDependencies": {
25
- "@payloadcms/translations": "^3.83.0",
26
- "@payloadcms/ui": "^3.83.0",
27
- "next": "15.4.11",
28
- "payload": "^3.83.0",
29
- "react": "19.2.5",
30
- "react-dom": "19.2.5"
25
+ "@payloadcms/translations": "^3.84.1",
26
+ "@payloadcms/ui": "^3.84.1",
27
+ "next": "^15.0.0 || ^16.0.0",
28
+ "payload": "^3.84.1",
29
+ "react": "19.2.6",
30
+ "react-dom": "19.2.6"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@payloadcms/eslint-config": "^3.28.0",
34
- "@payloadcms/translations": "^3.83.0",
34
+ "@payloadcms/translations": "^3.84.1",
35
35
  "@swc/cli": "^0.8.1",
36
- "@swc/core": "^1.15.26",
36
+ "@swc/core": "^1.15.33",
37
37
  "@types/react": "19.2.14",
38
38
  "@types/react-dom": "19.2.3",
39
39
  "copyfiles": "2.4.1",
40
40
  "eslint": "^9.0.0",
41
41
  "prettier": "^3.8.3",
42
42
  "rimraf": "6.1.3",
43
- "typescript": "5.9.3"
43
+ "typescript": "^6.0.3"
44
44
  },
45
45
  "files": [
46
46
  "dist"