@jhb.software/payload-alt-text-plugin 0.4.3 → 0.5.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 +62 -33
- package/dist/components/AltTextField.d.ts +1 -1
- package/dist/components/AltTextField.js +9 -0
- package/dist/components/AltTextField.js.map +1 -1
- package/dist/components/BulkGenerateAltTextsButton.js +5 -9
- package/dist/components/BulkGenerateAltTextsButton.js.map +1 -1
- package/dist/components/icons/ArrowRightIcon.js +6 -7
- package/dist/components/icons/ArrowRightIcon.js.map +1 -1
- package/dist/components/icons/CheckIcon.js +6 -7
- package/dist/components/icons/CheckIcon.js.map +1 -1
- package/dist/components/icons/ImageIcon.js +9 -26
- package/dist/components/icons/ImageIcon.js.map +1 -1
- package/dist/components/icons/Lightning.js +8 -4
- package/dist/components/icons/Lightning.js.map +1 -1
- package/dist/endpoints/bulkGenerateAltTexts.js +5 -0
- package/dist/endpoints/bulkGenerateAltTexts.js.map +1 -1
- package/dist/endpoints/generateAltText.js +9 -0
- package/dist/endpoints/generateAltText.js.map +1 -1
- package/dist/fields/altTextField.d.ts +7 -2
- package/dist/fields/altTextField.js +5 -19
- package/dist/fields/altTextField.js.map +1 -1
- package/dist/hooks/revalidateAltTextHealth.js +5 -0
- package/dist/hooks/revalidateAltTextHealth.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/plugin.js +23 -47
- package/dist/plugin.js.map +1 -1
- package/dist/translations/de.js +4 -5
- package/dist/translations/de.js.map +1 -1
- package/dist/translations/en.js +3 -4
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/translation-schema.json +4 -6
- package/dist/types/AltTextPluginConfig.d.ts +21 -5
- package/dist/types/AltTextPluginConfig.js.map +1 -1
- package/dist/utilities/altTextHealth.js +18 -10
- package/dist/utilities/altTextHealth.js.map +1 -1
- package/dist/utilities/mimeTypes.d.ts +70 -0
- package/dist/utilities/mimeTypes.js +115 -0
- package/dist/utilities/mimeTypes.js.map +1 -0
- package/package.json +14 -12
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { validateAltText } from '../utilities/mimeTypes.js';
|
|
1
2
|
import { translatedLabel } from '../utils/translatedLabel.js';
|
|
2
|
-
export function altTextField({ localized, supportedMimeTypes }) {
|
|
3
|
+
export function altTextField({ localized, supportedMimeTypes, trackedMimeTypes, validate }) {
|
|
3
4
|
return {
|
|
4
5
|
name: 'alt',
|
|
5
6
|
type: 'textarea',
|
|
@@ -8,29 +9,14 @@ export function altTextField({ localized, supportedMimeTypes }) {
|
|
|
8
9
|
Field: '@jhb.software/payload-alt-text-plugin/client#AltTextField'
|
|
9
10
|
},
|
|
10
11
|
custom: {
|
|
11
|
-
supportedMimeTypes
|
|
12
|
+
supportedMimeTypes,
|
|
13
|
+
trackedMimeTypes
|
|
12
14
|
}
|
|
13
15
|
},
|
|
14
16
|
label: translatedLabel('alternateText'),
|
|
15
17
|
localized,
|
|
16
18
|
required: true,
|
|
17
|
-
validate: (value,
|
|
18
|
-
// Since https://github.com/payloadcms/payload/pull/14988, when using external storage (e.g., S3),
|
|
19
|
-
// it is no longer possible to detect whether this validation runs during the initial upload
|
|
20
|
-
// or a regular update by checking the existence of the ID.
|
|
21
|
-
// Instead, compare the timestamps of the createdAt and updatedAt fields.
|
|
22
|
-
const isInitialUpload = operation === 'create' || 'createdAt' in data && 'updatedAt' in data && data.createdAt === data.updatedAt;
|
|
23
|
-
// initial upload: allow without alt text
|
|
24
|
-
if (isInitialUpload) {
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
// regular update: require alt text
|
|
28
|
-
if (!value || value.trim().length === 0) {
|
|
29
|
-
// @ts-expect-error - the translation key type does not include the custom key
|
|
30
|
-
return t('@jhb.software/payload-alt-text-plugin:theAlternateTextIsRequired');
|
|
31
|
-
}
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
19
|
+
validate: validate ?? ((value, args)=>validateAltText(value, args, trackedMimeTypes))
|
|
34
20
|
};
|
|
35
21
|
}
|
|
36
22
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/fields/altTextField.ts"],"sourcesContent":["import type { TextareaField } from 'payload'\n\nimport { translatedLabel } from '../utils/translatedLabel.js'\n\nexport function altTextField({\n localized,\n supportedMimeTypes,\n}: {\n localized?: TextareaField['localized']\n supportedMimeTypes?: string[]\n}): TextareaField {\n return {\n name: 'alt',\n type: 'textarea',\n admin: {\n components: {\n Field: '@jhb.software/payload-alt-text-plugin/client#AltTextField',\n },\n custom: {\n supportedMimeTypes,\n },\n },\n label: translatedLabel('alternateText'),\n localized,\n required: true,\n validate: (value,
|
|
1
|
+
{"version":3,"sources":["../../src/fields/altTextField.ts"],"sourcesContent":["import type { TextareaField, TextareaFieldValidation } from 'payload'\n\nimport { validateAltText } from '../utilities/mimeTypes.js'\nimport { translatedLabel } from '../utils/translatedLabel.js'\n\nexport function altTextField({\n localized,\n supportedMimeTypes,\n trackedMimeTypes,\n validate,\n}: {\n localized?: TextareaField['localized']\n /** MIME types the resolver can generate for — used to disable the Generate button. */\n supportedMimeTypes?: string[]\n /** MIME types for which alt text is tracked — used to hide the field and skip validation for others. */\n trackedMimeTypes?: string[]\n /** Custom validator that fully replaces the default. */\n validate?: TextareaFieldValidation\n}): TextareaField {\n return {\n name: 'alt',\n type: 'textarea',\n admin: {\n components: {\n Field: '@jhb.software/payload-alt-text-plugin/client#AltTextField',\n },\n custom: {\n supportedMimeTypes,\n trackedMimeTypes,\n },\n },\n label: translatedLabel('alternateText'),\n localized,\n required: true,\n validate: validate ?? ((value, args) => validateAltText(value, args, trackedMimeTypes)),\n }\n}\n"],"names":["validateAltText","translatedLabel","altTextField","localized","supportedMimeTypes","trackedMimeTypes","validate","name","type","admin","components","Field","custom","label","required","value","args"],"mappings":"AAEA,SAASA,eAAe,QAAQ,4BAA2B;AAC3D,SAASC,eAAe,QAAQ,8BAA6B;AAE7D,OAAO,SAASC,aAAa,EAC3BC,SAAS,EACTC,kBAAkB,EAClBC,gBAAgB,EAChBC,QAAQ,EAST;IACC,OAAO;QACLC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,YAAY;gBACVC,OAAO;YACT;YACAC,QAAQ;gBACNR;gBACAC;YACF;QACF;QACAQ,OAAOZ,gBAAgB;QACvBE;QACAW,UAAU;QACVR,UAAUA,YAAa,CAAA,CAACS,OAAOC,OAAShB,gBAAgBe,OAAOC,MAAMX,iBAAgB;IACvF;AACF"}
|
|
@@ -2,6 +2,11 @@ import { revalidateTag } from 'next/cache.js';
|
|
|
2
2
|
import { ALT_TEXT_HEALTH_PLUGIN_SLUG, getAltTextHealthCollectionTag } from '../utilities/altTextHealth.js';
|
|
3
3
|
function safeRevalidateTag(req, tag) {
|
|
4
4
|
try {
|
|
5
|
+
// Support both Next 15 and Next 16. Next 15 types `revalidateTag(tag)` as 1-arg; Next 16
|
|
6
|
+
// added a required second `profile` arg (a 1-arg call still works at runtime, with a
|
|
7
|
+
// deprecation warning). Cast so the build succeeds regardless of which Next types are
|
|
8
|
+
// resolved, while keeping runtime behaviour correct on both versions.
|
|
9
|
+
;
|
|
5
10
|
revalidateTag(tag);
|
|
6
11
|
} catch (error) {
|
|
7
12
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hooks/revalidateAltTextHealth.ts"],"sourcesContent":["import type { CollectionAfterChangeHook, CollectionAfterDeleteHook, PayloadRequest } from 'payload'\n\nimport { revalidateTag } from 'next/cache.js'\n\nimport {\n ALT_TEXT_HEALTH_PLUGIN_SLUG,\n getAltTextHealthCollectionTag,\n} from '../utilities/altTextHealth.js'\n\nfunction safeRevalidateTag(req: PayloadRequest, tag: string): void {\n try {\n revalidateTag(tag)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n\n if (message.includes('static generation store missing')) {\n req.payload.logger.warn({\n msg: 'Skipping alt text health cache revalidation outside a Next.js request context.',\n plugin: ALT_TEXT_HEALTH_PLUGIN_SLUG,\n tag,\n })\n return\n }\n\n throw error\n }\n}\n\nexport const createRevalidateAltTextHealthAfterChangeHook =\n (collectionSlug: string): CollectionAfterChangeHook =>\n ({ doc, req }) => {\n if (!req.context?.disableRevalidate) {\n safeRevalidateTag(req, getAltTextHealthCollectionTag(collectionSlug))\n }\n\n return doc\n }\n\nexport const createRevalidateAltTextHealthAfterDeleteHook =\n (collectionSlug: string): CollectionAfterDeleteHook =>\n ({ doc, req }) => {\n if (!req.context?.disableRevalidate) {\n safeRevalidateTag(req, getAltTextHealthCollectionTag(collectionSlug))\n }\n\n return doc\n }\n"],"names":["revalidateTag","ALT_TEXT_HEALTH_PLUGIN_SLUG","getAltTextHealthCollectionTag","safeRevalidateTag","req","tag","error","message","Error","String","includes","payload","logger","warn","msg","plugin","createRevalidateAltTextHealthAfterChangeHook","collectionSlug","doc","context","disableRevalidate","createRevalidateAltTextHealthAfterDeleteHook"],"mappings":"AAEA,SAASA,aAAa,QAAQ,gBAAe;AAE7C,SACEC,2BAA2B,EAC3BC,6BAA6B,QACxB,gCAA+B;AAEtC,SAASC,kBAAkBC,GAAmB,EAAEC,GAAW;IACzD,IAAI;
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/revalidateAltTextHealth.ts"],"sourcesContent":["import type { CollectionAfterChangeHook, CollectionAfterDeleteHook, PayloadRequest } from 'payload'\n\nimport { revalidateTag } from 'next/cache.js'\n\nimport {\n ALT_TEXT_HEALTH_PLUGIN_SLUG,\n getAltTextHealthCollectionTag,\n} from '../utilities/altTextHealth.js'\n\nfunction safeRevalidateTag(req: PayloadRequest, tag: string): void {\n try {\n // Support both Next 15 and Next 16. Next 15 types `revalidateTag(tag)` as 1-arg; Next 16\n // added a required second `profile` arg (a 1-arg call still works at runtime, with a\n // deprecation warning). Cast so the build succeeds regardless of which Next types are\n // resolved, while keeping runtime behaviour correct on both versions.\n ;(revalidateTag as (tag: string) => void)(tag)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n\n if (message.includes('static generation store missing')) {\n req.payload.logger.warn({\n msg: 'Skipping alt text health cache revalidation outside a Next.js request context.',\n plugin: ALT_TEXT_HEALTH_PLUGIN_SLUG,\n tag,\n })\n return\n }\n\n throw error\n }\n}\n\nexport const createRevalidateAltTextHealthAfterChangeHook =\n (collectionSlug: string): CollectionAfterChangeHook =>\n ({ doc, req }) => {\n if (!req.context?.disableRevalidate) {\n safeRevalidateTag(req, getAltTextHealthCollectionTag(collectionSlug))\n }\n\n return doc\n }\n\nexport const createRevalidateAltTextHealthAfterDeleteHook =\n (collectionSlug: string): CollectionAfterDeleteHook =>\n ({ doc, req }) => {\n if (!req.context?.disableRevalidate) {\n safeRevalidateTag(req, getAltTextHealthCollectionTag(collectionSlug))\n }\n\n return doc\n }\n"],"names":["revalidateTag","ALT_TEXT_HEALTH_PLUGIN_SLUG","getAltTextHealthCollectionTag","safeRevalidateTag","req","tag","error","message","Error","String","includes","payload","logger","warn","msg","plugin","createRevalidateAltTextHealthAfterChangeHook","collectionSlug","doc","context","disableRevalidate","createRevalidateAltTextHealthAfterDeleteHook"],"mappings":"AAEA,SAASA,aAAa,QAAQ,gBAAe;AAE7C,SACEC,2BAA2B,EAC3BC,6BAA6B,QACxB,gCAA+B;AAEtC,SAASC,kBAAkBC,GAAmB,EAAEC,GAAW;IACzD,IAAI;QACF,yFAAyF;QACzF,qFAAqF;QACrF,sFAAsF;QACtF,sEAAsE;;QACpEL,cAAwCK;IAC5C,EAAE,OAAOC,OAAO;QACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;QAEhE,IAAIC,QAAQG,QAAQ,CAAC,oCAAoC;YACvDN,IAAIO,OAAO,CAACC,MAAM,CAACC,IAAI,CAAC;gBACtBC,KAAK;gBACLC,QAAQd;gBACRI;YACF;YACA;QACF;QAEA,MAAMC;IACR;AACF;AAEA,OAAO,MAAMU,+CACX,CAACC,iBACD,CAAC,EAAEC,GAAG,EAAEd,GAAG,EAAE;QACX,IAAI,CAACA,IAAIe,OAAO,EAAEC,mBAAmB;YACnCjB,kBAAkBC,KAAKF,8BAA8Be;QACvD;QAEA,OAAOC;IACT,EAAC;AAEH,OAAO,MAAMG,+CACX,CAACJ,iBACD,CAAC,EAAEC,GAAG,EAAEd,GAAG,EAAE;QACX,IAAI,CAACA,IAAIe,OAAO,EAAEC,mBAAmB;YACnCjB,kBAAkBC,KAAKF,8BAA8Be;QACvD;QAEA,OAAOC;IACT,EAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { payloadAltTextPlugin } from './plugin.js';
|
|
2
2
|
export { openAIResolver } from './resolvers/openAI.js';
|
|
3
3
|
export * from './resolvers/types.js';
|
|
4
|
-
export type { IncomingAltTextPluginConfig as AltTextPluginConfig } from './types/AltTextPluginConfig.js';
|
|
4
|
+
export type { AltTextCollectionConfig, IncomingAltTextPluginConfig as AltTextPluginConfig, } from './types/AltTextPluginConfig.js';
|
|
5
|
+
export { matchesMimeType, validateAltText } from './utilities/mimeTypes.js';
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { payloadAltTextPlugin } from './plugin.js'\nexport { openAIResolver } from './resolvers/openAI.js'\nexport * from './resolvers/types.js'\nexport type {
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { payloadAltTextPlugin } from './plugin.js'\nexport { openAIResolver } from './resolvers/openAI.js'\nexport * from './resolvers/types.js'\nexport type {\n AltTextCollectionConfig,\n IncomingAltTextPluginConfig as AltTextPluginConfig,\n} from './types/AltTextPluginConfig.js'\nexport { matchesMimeType, validateAltText } from './utilities/mimeTypes.js'\n"],"names":["payloadAltTextPlugin","openAIResolver","matchesMimeType","validateAltText"],"mappings":"AAAA,SAASA,oBAAoB,QAAQ,cAAa;AAClD,SAASC,cAAc,QAAQ,wBAAuB;AACtD,cAAc,uBAAsB;AAKpC,SAASC,eAAe,EAAEC,eAAe,QAAQ,2BAA0B"}
|
package/dist/plugin.js
CHANGED
|
@@ -5,6 +5,7 @@ import { altTextField } from './fields/altTextField.js';
|
|
|
5
5
|
import { keywordsField } from './fields/keywordsField.js';
|
|
6
6
|
import { createRevalidateAltTextHealthAfterChangeHook, createRevalidateAltTextHealthAfterDeleteHook } from './hooks/revalidateAltTextHealth.js';
|
|
7
7
|
import { translations } from './translations/index.js';
|
|
8
|
+
import { normalizeCollectionsConfig } from './utilities/mimeTypes.js';
|
|
8
9
|
import { deepMergeSimple } from './utils/deepMergeSimple.js';
|
|
9
10
|
const altTextHealthWidgetDefinition = {
|
|
10
11
|
slug: 'alt-text-health',
|
|
@@ -18,36 +19,6 @@ const altTextHealthWidgetDefinition = {
|
|
|
18
19
|
maxWidth: 'full',
|
|
19
20
|
minWidth: 'medium'
|
|
20
21
|
};
|
|
21
|
-
const defaultAltTextHealthWidgetLayout = {
|
|
22
|
-
widgetSlug: 'alt-text-health',
|
|
23
|
-
width: 'full'
|
|
24
|
-
};
|
|
25
|
-
function appendAltTextHealthWidgetToLayout(layout) {
|
|
26
|
-
if (layout.some((widget)=>widget.widgetSlug === 'alt-text-health')) {
|
|
27
|
-
return layout;
|
|
28
|
-
}
|
|
29
|
-
return [
|
|
30
|
-
...layout,
|
|
31
|
-
defaultAltTextHealthWidgetLayout
|
|
32
|
-
];
|
|
33
|
-
}
|
|
34
|
-
function getDashboardDefaultLayout(defaultLayout) {
|
|
35
|
-
if (!defaultLayout) {
|
|
36
|
-
return [
|
|
37
|
-
{
|
|
38
|
-
widgetSlug: 'collections',
|
|
39
|
-
width: 'full'
|
|
40
|
-
},
|
|
41
|
-
defaultAltTextHealthWidgetLayout
|
|
42
|
-
];
|
|
43
|
-
}
|
|
44
|
-
if (Array.isArray(defaultLayout)) {
|
|
45
|
-
return appendAltTextHealthWidgetToLayout(defaultLayout);
|
|
46
|
-
}
|
|
47
|
-
return async ({ req })=>appendAltTextHealthWidgetToLayout(await defaultLayout({
|
|
48
|
-
req
|
|
49
|
-
}));
|
|
50
|
-
}
|
|
51
22
|
export const payloadAltTextPlugin = (incomingPluginConfig)=>(incomingConfig)=>{
|
|
52
23
|
const config = {
|
|
53
24
|
...incomingConfig
|
|
@@ -58,9 +29,10 @@ export const payloadAltTextPlugin = (incomingPluginConfig)=>(incomingConfig)=>{
|
|
|
58
29
|
}
|
|
59
30
|
const locales = config.localization ? config.localization.locales.map((localeConfig)=>typeof localeConfig === 'string' ? localeConfig : localeConfig.code) : [];
|
|
60
31
|
const enableHealthCheck = incomingPluginConfig.healthCheck !== false;
|
|
32
|
+
const normalizedCollections = normalizeCollectionsConfig(incomingPluginConfig.collections);
|
|
61
33
|
const pluginConfig = {
|
|
62
34
|
access: incomingPluginConfig.access ?? (({ req })=>!!req.user),
|
|
63
|
-
collections:
|
|
35
|
+
collections: normalizedCollections,
|
|
64
36
|
enabled: incomingPluginConfig.enabled ?? true,
|
|
65
37
|
fieldsOverride: incomingPluginConfig.fieldsOverride,
|
|
66
38
|
getImageThumbnail: incomingPluginConfig.getImageThumbnail,
|
|
@@ -74,27 +46,34 @@ export const payloadAltTextPlugin = (incomingPluginConfig)=>(incomingConfig)=>{
|
|
|
74
46
|
if (locales.length === 0 && !incomingPluginConfig.locale) {
|
|
75
47
|
throw new Error('The alt-text plugin requires a "locale" option when Payload localization is disabled. ' + 'Please add { locale: "en" } (or your preferred locale) to your plugin configuration.');
|
|
76
48
|
}
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}),
|
|
82
|
-
keywordsField({
|
|
83
|
-
localized: Boolean(config.localization)
|
|
84
|
-
})
|
|
85
|
-
];
|
|
86
|
-
const fields = incomingPluginConfig.fieldsOverride && typeof incomingPluginConfig.fieldsOverride === 'function' ? incomingPluginConfig.fieldsOverride({
|
|
87
|
-
defaultFields
|
|
88
|
-
}) : defaultFields;
|
|
49
|
+
const collectionConfigBySlug = new Map(normalizedCollections.map((entry)=>[
|
|
50
|
+
entry.slug,
|
|
51
|
+
entry
|
|
52
|
+
]));
|
|
89
53
|
// Ensure collections array exists
|
|
90
54
|
config.collections = config.collections || [];
|
|
91
55
|
// Map over collections and inject AI alt text fields into specified ones
|
|
92
56
|
config.collections = config.collections.map((collectionConfig)=>{
|
|
93
|
-
|
|
57
|
+
const altTextCollectionConfig = collectionConfigBySlug.get(collectionConfig.slug);
|
|
58
|
+
if (altTextCollectionConfig) {
|
|
94
59
|
if (!collectionConfig.upload) {
|
|
95
60
|
console.warn(`AI Alt Text Plugin: Collection "${collectionConfig.slug}" is not an upload collection. Skipping field injection.`);
|
|
96
61
|
return collectionConfig;
|
|
97
62
|
}
|
|
63
|
+
const defaultFields = [
|
|
64
|
+
altTextField({
|
|
65
|
+
localized: Boolean(config.localization),
|
|
66
|
+
supportedMimeTypes: pluginConfig.resolver.supportedMimeTypes,
|
|
67
|
+
trackedMimeTypes: altTextCollectionConfig.mimeTypes,
|
|
68
|
+
validate: altTextCollectionConfig.validate
|
|
69
|
+
}),
|
|
70
|
+
keywordsField({
|
|
71
|
+
localized: Boolean(config.localization)
|
|
72
|
+
})
|
|
73
|
+
];
|
|
74
|
+
const fields = incomingPluginConfig.fieldsOverride && typeof incomingPluginConfig.fieldsOverride === 'function' ? incomingPluginConfig.fieldsOverride({
|
|
75
|
+
defaultFields
|
|
76
|
+
}) : defaultFields;
|
|
98
77
|
return {
|
|
99
78
|
...collectionConfig,
|
|
100
79
|
admin: {
|
|
@@ -151,9 +130,6 @@ export const payloadAltTextPlugin = (incomingPluginConfig)=>(incomingConfig)=>{
|
|
|
151
130
|
...config.admin,
|
|
152
131
|
dashboard: {
|
|
153
132
|
...config.admin?.dashboard,
|
|
154
|
-
...enableHealthCheck && {
|
|
155
|
-
defaultLayout: getDashboardDefaultLayout(config.admin?.dashboard?.defaultLayout)
|
|
156
|
-
},
|
|
157
133
|
widgets
|
|
158
134
|
}
|
|
159
135
|
},
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { Config, PayloadRequest, Widget, WidgetInstance } from 'payload'\n\nimport type {\n AltTextPluginConfig,\n IncomingAltTextPluginConfig,\n} from './types/AltTextPluginConfig.js'\n\nimport { altTextHealthEndpoint } from './endpoints/altTextHealth.js'\nimport { bulkGenerateAltTextsEndpoint } from './endpoints/bulkGenerateAltTexts.js'\nimport { generateAltTextEndpoint } from './endpoints/generateAltText.js'\nimport { altTextField } from './fields/altTextField.js'\nimport { keywordsField } from './fields/keywordsField.js'\nimport {\n createRevalidateAltTextHealthAfterChangeHook,\n createRevalidateAltTextHealthAfterDeleteHook,\n} from './hooks/revalidateAltTextHealth.js'\nimport { translations } from './translations/index.js'\nimport { deepMergeSimple } from './utils/deepMergeSimple.js'\n\nconst altTextHealthWidgetDefinition = {\n slug: 'alt-text-health',\n // `Component` was renamed from `ComponentPath` in Payload 3.79.0. Set both for backward compatibility.\n Component: '@jhb.software/payload-alt-text-plugin/server#AltTextHealthWidget',\n ComponentPath: '@jhb.software/payload-alt-text-plugin/server#AltTextHealthWidget',\n label: {\n de: 'Alternativtexte Zustand',\n en: 'Alt text health',\n },\n maxWidth: 'full',\n minWidth: 'medium',\n} satisfies { ComponentPath: string } & Widget\n\ntype DashboardDefaultLayout = Config['admin'] extends infer TAdmin\n ? TAdmin extends { dashboard?: infer TDashboard }\n ? TDashboard extends { defaultLayout?: infer TDefaultLayout }\n ? TDefaultLayout\n : never\n : never\n : never\n\nconst defaultAltTextHealthWidgetLayout: WidgetInstance = {\n widgetSlug: 'alt-text-health',\n width: 'full',\n}\n\nfunction appendAltTextHealthWidgetToLayout(layout: WidgetInstance[]): WidgetInstance[] {\n if (layout.some((widget) => widget.widgetSlug === 'alt-text-health')) {\n return layout\n }\n\n return [...layout, defaultAltTextHealthWidgetLayout]\n}\n\nfunction getDashboardDefaultLayout(defaultLayout: DashboardDefaultLayout | undefined) {\n if (!defaultLayout) {\n return [\n {\n widgetSlug: 'collections',\n width: 'full',\n },\n defaultAltTextHealthWidgetLayout,\n ] satisfies WidgetInstance[]\n }\n\n if (Array.isArray(defaultLayout)) {\n return appendAltTextHealthWidgetToLayout(defaultLayout)\n }\n\n return async ({ req }: { req: PayloadRequest }) =>\n appendAltTextHealthWidgetToLayout(await defaultLayout({ req }))\n}\n\nexport const payloadAltTextPlugin =\n (incomingPluginConfig: IncomingAltTextPluginConfig) =>\n (incomingConfig: Config): Config => {\n const config = { ...incomingConfig }\n\n // If the plugin is disabled, return the config without modifying it\n if (incomingPluginConfig.enabled === false) {\n return config\n }\n\n const locales = config.localization\n ? config.localization.locales.map((localeConfig) =>\n typeof localeConfig === 'string' ? localeConfig : localeConfig.code,\n )\n : []\n\n const enableHealthCheck = incomingPluginConfig.healthCheck !== false\n\n const pluginConfig: AltTextPluginConfig = {\n access: incomingPluginConfig.access ?? (({ req }) => !!req.user),\n collections: incomingPluginConfig.collections,\n enabled: incomingPluginConfig.enabled ?? true,\n fieldsOverride: incomingPluginConfig.fieldsOverride,\n getImageThumbnail: incomingPluginConfig.getImageThumbnail,\n healthCheck: enableHealthCheck,\n locale: incomingPluginConfig.locale,\n locales,\n maxBulkGenerateConcurrency: incomingPluginConfig.maxBulkGenerateConcurrency ?? 16,\n resolver: incomingPluginConfig.resolver,\n }\n\n // Validate locale requirement for non-localized mode\n if (locales.length === 0 && !incomingPluginConfig.locale) {\n throw new Error(\n 'The alt-text plugin requires a \"locale\" option when Payload localization is disabled. ' +\n 'Please add { locale: \"en\" } (or your preferred locale) to your plugin configuration.',\n )\n }\n\n const defaultFields = [\n altTextField({\n localized: Boolean(config.localization),\n supportedMimeTypes: pluginConfig.resolver.supportedMimeTypes,\n }),\n keywordsField({\n localized: Boolean(config.localization),\n }),\n ]\n\n const fields =\n incomingPluginConfig.fieldsOverride &&\n typeof incomingPluginConfig.fieldsOverride === 'function'\n ? incomingPluginConfig.fieldsOverride({ defaultFields })\n : defaultFields\n\n // Ensure collections array exists\n config.collections = config.collections || []\n\n // Map over collections and inject AI alt text fields into specified ones\n config.collections = config.collections.map((collectionConfig) => {\n if (pluginConfig.collections.includes(collectionConfig.slug)) {\n if (!collectionConfig.upload) {\n console.warn(\n `AI Alt Text Plugin: Collection \"${collectionConfig.slug}\" is not an upload collection. Skipping field injection.`,\n )\n return collectionConfig\n }\n\n return {\n ...collectionConfig,\n admin: {\n ...collectionConfig.admin,\n components: {\n ...(collectionConfig.admin?.components ?? {}),\n // TODO: use the beforeBulkAction custom component slot once available: https://github.com/payloadcms/payload/pull/11719\n beforeListTable: [\n ...(collectionConfig.admin?.components?.beforeListTable ?? []),\n {\n path: '@jhb.software/payload-alt-text-plugin/client#BulkGenerateAltTextsButton',\n props: {\n collectionSlug: collectionConfig.slug,\n },\n },\n ],\n },\n // enhance the search by adding the filename, keywords and alt fields (if the user has not provided their own listSearchableFields)\n listSearchableFields: collectionConfig.admin?.listSearchableFields ?? [\n 'filename',\n 'keywords',\n 'alt',\n ],\n },\n fields: [...(collectionConfig.fields ?? []), ...fields],\n hooks: {\n ...collectionConfig.hooks,\n ...(enableHealthCheck && {\n afterChange: [\n ...(collectionConfig.hooks?.afterChange ?? []),\n createRevalidateAltTextHealthAfterChangeHook(collectionConfig.slug),\n ],\n afterDelete: [\n ...(collectionConfig.hooks?.afterDelete ?? []),\n createRevalidateAltTextHealthAfterDeleteHook(collectionConfig.slug),\n ],\n }),\n },\n }\n }\n\n return collectionConfig\n })\n\n const existingWidgets = config.admin?.dashboard?.widgets ?? []\n const widgets =\n !enableHealthCheck || existingWidgets.some((widget) => widget.slug === 'alt-text-health')\n ? existingWidgets\n : [...existingWidgets, altTextHealthWidgetDefinition]\n\n return {\n ...config,\n admin: {\n ...config.admin,\n dashboard: {\n ...config.admin?.dashboard,\n ...(enableHealthCheck && {\n defaultLayout: getDashboardDefaultLayout(config.admin?.dashboard?.defaultLayout),\n }),\n widgets,\n },\n },\n custom: {\n ...config.custom,\n // Make plugin config available in hooks/actions\n altTextPluginConfig: pluginConfig,\n },\n endpoints: [\n ...(config.endpoints ?? []),\n {\n handler: generateAltTextEndpoint(pluginConfig.access),\n method: 'post',\n path: '/alt-text-plugin/generate',\n },\n {\n handler: bulkGenerateAltTextsEndpoint(pluginConfig.access),\n method: 'post',\n path: '/alt-text-plugin/generate/bulk',\n },\n ...(enableHealthCheck\n ? [\n {\n handler: altTextHealthEndpoint(pluginConfig.access),\n method: 'get' as const,\n path: '/alt-text-plugin/health',\n },\n ]\n : []),\n ],\n i18n: {\n ...config.i18n,\n translations: deepMergeSimple(translations, incomingConfig.i18n?.translations ?? {}),\n },\n }\n }\n"],"names":["altTextHealthEndpoint","bulkGenerateAltTextsEndpoint","generateAltTextEndpoint","altTextField","keywordsField","createRevalidateAltTextHealthAfterChangeHook","createRevalidateAltTextHealthAfterDeleteHook","translations","deepMergeSimple","altTextHealthWidgetDefinition","slug","Component","ComponentPath","label","de","en","maxWidth","minWidth","defaultAltTextHealthWidgetLayout","widgetSlug","width","appendAltTextHealthWidgetToLayout","layout","some","widget","getDashboardDefaultLayout","defaultLayout","Array","isArray","req","payloadAltTextPlugin","incomingPluginConfig","incomingConfig","config","enabled","locales","localization","map","localeConfig","code","enableHealthCheck","healthCheck","pluginConfig","access","user","collections","fieldsOverride","getImageThumbnail","locale","maxBulkGenerateConcurrency","resolver","length","Error","defaultFields","localized","Boolean","supportedMimeTypes","fields","collectionConfig","includes","upload","console","warn","admin","components","beforeListTable","path","props","collectionSlug","listSearchableFields","hooks","afterChange","afterDelete","existingWidgets","dashboard","widgets","custom","altTextPluginConfig","endpoints","handler","method","i18n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { Config, Widget } from 'payload'\n\nimport type {\n AltTextPluginConfig,\n IncomingAltTextPluginConfig,\n} from './types/AltTextPluginConfig.js'\n\nimport { altTextHealthEndpoint } from './endpoints/altTextHealth.js'\nimport { bulkGenerateAltTextsEndpoint } from './endpoints/bulkGenerateAltTexts.js'\nimport { generateAltTextEndpoint } from './endpoints/generateAltText.js'\nimport { altTextField } from './fields/altTextField.js'\nimport { keywordsField } from './fields/keywordsField.js'\nimport {\n createRevalidateAltTextHealthAfterChangeHook,\n createRevalidateAltTextHealthAfterDeleteHook,\n} from './hooks/revalidateAltTextHealth.js'\nimport { translations } from './translations/index.js'\nimport { normalizeCollectionsConfig } from './utilities/mimeTypes.js'\nimport { deepMergeSimple } from './utils/deepMergeSimple.js'\n\nconst altTextHealthWidgetDefinition = {\n slug: 'alt-text-health',\n // `Component` was renamed from `ComponentPath` in Payload 3.79.0. Set both for backward compatibility.\n Component: '@jhb.software/payload-alt-text-plugin/server#AltTextHealthWidget',\n ComponentPath: '@jhb.software/payload-alt-text-plugin/server#AltTextHealthWidget',\n label: {\n de: 'Alternativtexte Zustand',\n en: 'Alt text health',\n },\n maxWidth: 'full',\n minWidth: 'medium',\n} satisfies { ComponentPath: string } & Widget\n\nexport const payloadAltTextPlugin =\n (incomingPluginConfig: IncomingAltTextPluginConfig) =>\n (incomingConfig: Config): Config => {\n const config = { ...incomingConfig }\n\n // If the plugin is disabled, return the config without modifying it\n if (incomingPluginConfig.enabled === false) {\n return config\n }\n\n const locales = config.localization\n ? config.localization.locales.map((localeConfig) =>\n typeof localeConfig === 'string' ? localeConfig : localeConfig.code,\n )\n : []\n\n const enableHealthCheck = incomingPluginConfig.healthCheck !== false\n\n const normalizedCollections = normalizeCollectionsConfig(incomingPluginConfig.collections)\n\n const pluginConfig: AltTextPluginConfig = {\n access: incomingPluginConfig.access ?? (({ req }) => !!req.user),\n collections: normalizedCollections,\n enabled: incomingPluginConfig.enabled ?? true,\n fieldsOverride: incomingPluginConfig.fieldsOverride,\n getImageThumbnail: incomingPluginConfig.getImageThumbnail,\n healthCheck: enableHealthCheck,\n locale: incomingPluginConfig.locale,\n locales,\n maxBulkGenerateConcurrency: incomingPluginConfig.maxBulkGenerateConcurrency ?? 16,\n resolver: incomingPluginConfig.resolver,\n }\n\n // Validate locale requirement for non-localized mode\n if (locales.length === 0 && !incomingPluginConfig.locale) {\n throw new Error(\n 'The alt-text plugin requires a \"locale\" option when Payload localization is disabled. ' +\n 'Please add { locale: \"en\" } (or your preferred locale) to your plugin configuration.',\n )\n }\n\n const collectionConfigBySlug = new Map<string, (typeof normalizedCollections)[number]>(\n normalizedCollections.map((entry) => [entry.slug, entry]),\n )\n\n // Ensure collections array exists\n config.collections = config.collections || []\n\n // Map over collections and inject AI alt text fields into specified ones\n config.collections = config.collections.map((collectionConfig) => {\n const altTextCollectionConfig = collectionConfigBySlug.get(collectionConfig.slug)\n\n if (altTextCollectionConfig) {\n if (!collectionConfig.upload) {\n console.warn(\n `AI Alt Text Plugin: Collection \"${collectionConfig.slug}\" is not an upload collection. Skipping field injection.`,\n )\n return collectionConfig\n }\n\n const defaultFields = [\n altTextField({\n localized: Boolean(config.localization),\n supportedMimeTypes: pluginConfig.resolver.supportedMimeTypes,\n trackedMimeTypes: altTextCollectionConfig.mimeTypes,\n validate: altTextCollectionConfig.validate,\n }),\n keywordsField({\n localized: Boolean(config.localization),\n }),\n ]\n\n const fields =\n incomingPluginConfig.fieldsOverride &&\n typeof incomingPluginConfig.fieldsOverride === 'function'\n ? incomingPluginConfig.fieldsOverride({ defaultFields })\n : defaultFields\n\n return {\n ...collectionConfig,\n admin: {\n ...collectionConfig.admin,\n components: {\n ...(collectionConfig.admin?.components ?? {}),\n // TODO: use the beforeBulkAction custom component slot once available: https://github.com/payloadcms/payload/pull/11719\n beforeListTable: [\n ...(collectionConfig.admin?.components?.beforeListTable ?? []),\n {\n path: '@jhb.software/payload-alt-text-plugin/client#BulkGenerateAltTextsButton',\n props: {\n collectionSlug: collectionConfig.slug,\n },\n },\n ],\n },\n // enhance the search by adding the filename, keywords and alt fields (if the user has not provided their own listSearchableFields)\n listSearchableFields: collectionConfig.admin?.listSearchableFields ?? [\n 'filename',\n 'keywords',\n 'alt',\n ],\n },\n fields: [...(collectionConfig.fields ?? []), ...fields],\n hooks: {\n ...collectionConfig.hooks,\n ...(enableHealthCheck && {\n afterChange: [\n ...(collectionConfig.hooks?.afterChange ?? []),\n createRevalidateAltTextHealthAfterChangeHook(collectionConfig.slug),\n ],\n afterDelete: [\n ...(collectionConfig.hooks?.afterDelete ?? []),\n createRevalidateAltTextHealthAfterDeleteHook(collectionConfig.slug),\n ],\n }),\n },\n }\n }\n\n return collectionConfig\n })\n\n const existingWidgets = config.admin?.dashboard?.widgets ?? []\n const widgets =\n !enableHealthCheck || existingWidgets.some((widget) => widget.slug === 'alt-text-health')\n ? existingWidgets\n : [...existingWidgets, altTextHealthWidgetDefinition]\n\n return {\n ...config,\n admin: {\n ...config.admin,\n dashboard: {\n ...config.admin?.dashboard,\n widgets,\n },\n },\n custom: {\n ...config.custom,\n // Make plugin config available in hooks/actions\n altTextPluginConfig: pluginConfig,\n },\n endpoints: [\n ...(config.endpoints ?? []),\n {\n handler: generateAltTextEndpoint(pluginConfig.access),\n method: 'post',\n path: '/alt-text-plugin/generate',\n },\n {\n handler: bulkGenerateAltTextsEndpoint(pluginConfig.access),\n method: 'post',\n path: '/alt-text-plugin/generate/bulk',\n },\n ...(enableHealthCheck\n ? [\n {\n handler: altTextHealthEndpoint(pluginConfig.access),\n method: 'get' as const,\n path: '/alt-text-plugin/health',\n },\n ]\n : []),\n ],\n i18n: {\n ...config.i18n,\n translations: deepMergeSimple(translations, incomingConfig.i18n?.translations ?? {}),\n },\n }\n }\n"],"names":["altTextHealthEndpoint","bulkGenerateAltTextsEndpoint","generateAltTextEndpoint","altTextField","keywordsField","createRevalidateAltTextHealthAfterChangeHook","createRevalidateAltTextHealthAfterDeleteHook","translations","normalizeCollectionsConfig","deepMergeSimple","altTextHealthWidgetDefinition","slug","Component","ComponentPath","label","de","en","maxWidth","minWidth","payloadAltTextPlugin","incomingPluginConfig","incomingConfig","config","enabled","locales","localization","map","localeConfig","code","enableHealthCheck","healthCheck","normalizedCollections","collections","pluginConfig","access","req","user","fieldsOverride","getImageThumbnail","locale","maxBulkGenerateConcurrency","resolver","length","Error","collectionConfigBySlug","Map","entry","collectionConfig","altTextCollectionConfig","get","upload","console","warn","defaultFields","localized","Boolean","supportedMimeTypes","trackedMimeTypes","mimeTypes","validate","fields","admin","components","beforeListTable","path","props","collectionSlug","listSearchableFields","hooks","afterChange","afterDelete","existingWidgets","dashboard","widgets","some","widget","custom","altTextPluginConfig","endpoints","handler","method","i18n"],"mappings":"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"}
|
package/dist/translations/de.js
CHANGED
|
@@ -7,9 +7,8 @@ export const de = {
|
|
|
7
7
|
keywordsDescription: 'Schlüsselwörter, die das Bild beschreiben. Wird bei der Suche nach dem Bild verwendet.',
|
|
8
8
|
// Button labels
|
|
9
9
|
generateAltText: 'Alternativtext generieren',
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
images: 'Bilder',
|
|
10
|
+
generateAltTextFor_one: 'Alternativtext für {{count}} Bild generieren',
|
|
11
|
+
generateAltTextFor_other: 'Alternativtext für {{count}} Bilder generieren',
|
|
13
12
|
// Toast messages
|
|
14
13
|
altTextGeneratedSuccess: 'Alternativtext erfolgreich generiert. Bitte überprüfen und speichern Sie das Dokument.',
|
|
15
14
|
cannotGenerateMissingFields: 'Alternativtext kann nicht generiert werden. Erforderliche Felder fehlen.',
|
|
@@ -29,8 +28,8 @@ export const de = {
|
|
|
29
28
|
// Validation messages
|
|
30
29
|
theAlternateTextIsRequired: 'Der Alternativtext ist erforderlich.',
|
|
31
30
|
// Dashboard widget
|
|
32
|
-
altTextHealthDescription: 'Alternativtext
|
|
33
|
-
altTextHealthWidget: '
|
|
31
|
+
altTextHealthDescription: 'Alternativtext Abdeckung in allen Upload-Sammlungen.',
|
|
32
|
+
altTextHealthWidget: 'Alternativtexte',
|
|
34
33
|
collectionCheckFailed: 'Status nicht verfügbar',
|
|
35
34
|
healthCheckPartialWarning: 'Einige Sammlungen konnten gerade nicht geprüft werden.',
|
|
36
35
|
localeCount: '{{count}} Sprachen',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/translations/de.ts"],"sourcesContent":["import type { GenericTranslationsObject } from './index.js'\n\nexport const de: GenericTranslationsObject = {\n $schema: './translation-schema.json',\n '@jhb.software/payload-alt-text-plugin': {\n // Field labels\n alternateText: 'Alternativtext',\n keywords: 'Schlüsselwörter',\n keywordsDescription:\n 'Schlüsselwörter, die das Bild beschreiben. Wird bei der Suche nach dem Bild verwendet.',\n\n // Button labels\n generateAltText: 'Alternativtext generieren',\n
|
|
1
|
+
{"version":3,"sources":["../../src/translations/de.ts"],"sourcesContent":["import type { GenericTranslationsObject } from './index.js'\n\nexport const de: GenericTranslationsObject = {\n $schema: './translation-schema.json',\n '@jhb.software/payload-alt-text-plugin': {\n // Field labels\n alternateText: 'Alternativtext',\n keywords: 'Schlüsselwörter',\n keywordsDescription:\n 'Schlüsselwörter, die das Bild beschreiben. Wird bei der Suche nach dem Bild verwendet.',\n\n // Button labels\n generateAltText: 'Alternativtext generieren',\n generateAltTextFor_one: 'Alternativtext für {{count}} Bild generieren',\n generateAltTextFor_other: 'Alternativtext für {{count}} Bilder generieren',\n\n // Toast messages\n altTextGeneratedSuccess:\n 'Alternativtext erfolgreich generiert. Bitte überprüfen und speichern Sie das Dokument.',\n cannotGenerateMissingFields:\n 'Alternativtext kann nicht generiert werden. Erforderliche Felder fehlen.',\n errorGeneratingAltText:\n 'Fehler beim Generieren des Alternativtextes. Bitte versuchen Sie es erneut.',\n failedToGenerate:\n 'Generierung des Alternativtextes fehlgeschlagen. Bitte versuchen Sie es erneut.',\n failedToGenerateForXImages:\n 'Generierung des Alternativtextes für {{count}} Bilder fehlgeschlagen.',\n noAltTextGenerated: 'Kein Alternativtext generiert. Bitte versuchen Sie es erneut.',\n xOfYImagesUpdated: '{{updated}} von {{total}} Bildern aktualisiert.',\n\n // Help text\n altTextDescription:\n 'Alternativtext für das Bild. Dieser wird für Screenreader und SEO verwendet. Er sollte die folgenden Anforderungen erfüllen:',\n altTextRequirement1: 'Beschreibt in 1-2 Sätzen, was auf dem Bild zu sehen ist.',\n altTextRequirement2:\n 'Sollte möglichst die gleichen Informationen oder den gleichen Zweck wie das Bild vermitteln.',\n altTextRequirement3:\n 'Phrasen wie \"Bild von\" oder \"Foto von\" sind überflüssig, da Screenreader bereits anzeigen, dass es sich um ein Bild handelt.',\n\n // Tooltips\n pleaseSaveDocumentFirst: 'Bitte speichern Sie zuerst das Dokument',\n unsupportedMimeType:\n 'Alternativtext-Generierung wird für {{mimeType}}-Dateien nicht unterstützt',\n\n // Validation messages\n theAlternateTextIsRequired: 'Der Alternativtext ist erforderlich.',\n\n // Dashboard widget\n altTextHealthDescription: 'Alternativtext Abdeckung in allen Upload-Sammlungen.',\n altTextHealthWidget: 'Alternativtexte',\n collectionCheckFailed: 'Status nicht verfügbar',\n healthCheckPartialWarning: 'Einige Sammlungen konnten gerade nicht geprüft werden.',\n localeCount: '{{count}} Sprachen',\n noImagesFound: 'In den konfigurierten Sammlungen wurden noch keine Bilder gefunden.',\n statusHealthy: 'Alle vorhanden',\n statusUnhealthy: '{{count}} fehlend',\n totalImageCount: '{{count}} Bilder',\n },\n}\n"],"names":["de","$schema","alternateText","keywords","keywordsDescription","generateAltText","generateAltTextFor_one","generateAltTextFor_other","altTextGeneratedSuccess","cannotGenerateMissingFields","errorGeneratingAltText","failedToGenerate","failedToGenerateForXImages","noAltTextGenerated","xOfYImagesUpdated","altTextDescription","altTextRequirement1","altTextRequirement2","altTextRequirement3","pleaseSaveDocumentFirst","unsupportedMimeType","theAlternateTextIsRequired","altTextHealthDescription","altTextHealthWidget","collectionCheckFailed","healthCheckPartialWarning","localeCount","noImagesFound","statusHealthy","statusUnhealthy","totalImageCount"],"mappings":"AAEA,OAAO,MAAMA,KAAgC;IAC3CC,SAAS;IACT,yCAAyC;QACvC,eAAe;QACfC,eAAe;QACfC,UAAU;QACVC,qBACE;QAEF,gBAAgB;QAChBC,iBAAiB;QACjBC,wBAAwB;QACxBC,0BAA0B;QAE1B,iBAAiB;QACjBC,yBACE;QACFC,6BACE;QACFC,wBACE;QACFC,kBACE;QACFC,4BACE;QACFC,oBAAoB;QACpBC,mBAAmB;QAEnB,YAAY;QACZC,oBACE;QACFC,qBAAqB;QACrBC,qBACE;QACFC,qBACE;QAEF,WAAW;QACXC,yBAAyB;QACzBC,qBACE;QAEF,sBAAsB;QACtBC,4BAA4B;QAE5B,mBAAmB;QACnBC,0BAA0B;QAC1BC,qBAAqB;QACrBC,uBAAuB;QACvBC,2BAA2B;QAC3BC,aAAa;QACbC,eAAe;QACfC,eAAe;QACfC,iBAAiB;QACjBC,iBAAiB;IACnB;AACF,EAAC"}
|
package/dist/translations/en.js
CHANGED
|
@@ -7,9 +7,8 @@ export const en = {
|
|
|
7
7
|
keywordsDescription: 'Keywords which describe the image. Used when searching for the image.',
|
|
8
8
|
// Button labels
|
|
9
9
|
generateAltText: 'Generate alt text',
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
images: 'images',
|
|
10
|
+
generateAltTextFor_one: 'Generate alt text for {{count}} image',
|
|
11
|
+
generateAltTextFor_other: 'Generate alt text for {{count}} images',
|
|
13
12
|
// Toast messages
|
|
14
13
|
altTextGeneratedSuccess: 'Alt text generated successfully. Please review and save the document.',
|
|
15
14
|
cannotGenerateMissingFields: 'Cannot generate alt text. Missing required fields.',
|
|
@@ -30,7 +29,7 @@ export const en = {
|
|
|
30
29
|
theAlternateTextIsRequired: 'An alternate text is required.',
|
|
31
30
|
// Dashboard widget
|
|
32
31
|
altTextHealthDescription: 'Alt text coverage across upload collections.',
|
|
33
|
-
altTextHealthWidget: 'Alt
|
|
32
|
+
altTextHealthWidget: 'Alt Texts',
|
|
34
33
|
collectionCheckFailed: 'Status unavailable',
|
|
35
34
|
healthCheckPartialWarning: 'Some collections could not be checked right now.',
|
|
36
35
|
localeCount: '{{count}} locales',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/translations/en.ts"],"sourcesContent":["import type { GenericTranslationsObject } from './index.js'\n\nexport const en: GenericTranslationsObject = {\n $schema: './translation-schema.json',\n '@jhb.software/payload-alt-text-plugin': {\n // Field labels\n alternateText: 'Alternate text',\n keywords: 'Keywords',\n keywordsDescription: 'Keywords which describe the image. Used when searching for the image.',\n\n // Button labels\n generateAltText: 'Generate alt text',\n
|
|
1
|
+
{"version":3,"sources":["../../src/translations/en.ts"],"sourcesContent":["import type { GenericTranslationsObject } from './index.js'\n\nexport const en: GenericTranslationsObject = {\n $schema: './translation-schema.json',\n '@jhb.software/payload-alt-text-plugin': {\n // Field labels\n alternateText: 'Alternate text',\n keywords: 'Keywords',\n keywordsDescription: 'Keywords which describe the image. Used when searching for the image.',\n\n // Button labels\n generateAltText: 'Generate alt text',\n generateAltTextFor_one: 'Generate alt text for {{count}} image',\n generateAltTextFor_other: 'Generate alt text for {{count}} images',\n\n // Toast messages\n altTextGeneratedSuccess:\n 'Alt text generated successfully. Please review and save the document.',\n cannotGenerateMissingFields: 'Cannot generate alt text. Missing required fields.',\n errorGeneratingAltText: 'Error generating alt text. Please try again.',\n failedToGenerate: 'Failed to generate alt text. Please try again.',\n failedToGenerateForXImages: 'Failed to generate alt text for {{count}} images.',\n noAltTextGenerated: 'No alt text generated. Please try again.',\n xOfYImagesUpdated: '{{updated}} of {{total}} images updated.',\n\n // Help text\n altTextDescription:\n 'Alternate text for the image. This will be used for screen readers and SEO. It should meet the following requirements:',\n altTextRequirement1: 'Describes in 1-2 sentences, what is visible in the image.',\n altTextRequirement2:\n 'Should convey the same information or purpose as the image, whenever possible.',\n altTextRequirement3:\n 'Phrases like \"image of\" or \"picture of\" are unnecessary, since screen readers already announce that it\\'s an image.',\n\n // Tooltips\n pleaseSaveDocumentFirst: 'Please save the document first',\n unsupportedMimeType: 'Alt text generation is not supported for {{mimeType}} files',\n\n // Validation messages\n theAlternateTextIsRequired: 'An alternate text is required.',\n\n // Dashboard widget\n altTextHealthDescription: 'Alt text coverage across upload collections.',\n altTextHealthWidget: 'Alt Texts',\n collectionCheckFailed: 'Status unavailable',\n healthCheckPartialWarning: 'Some collections could not be checked right now.',\n localeCount: '{{count}} locales',\n noImagesFound: 'No images found in the configured collections yet.',\n statusHealthy: 'All set',\n statusUnhealthy: '{{count}} missing',\n totalImageCount: '{{count}} images',\n },\n}\n"],"names":["en","$schema","alternateText","keywords","keywordsDescription","generateAltText","generateAltTextFor_one","generateAltTextFor_other","altTextGeneratedSuccess","cannotGenerateMissingFields","errorGeneratingAltText","failedToGenerate","failedToGenerateForXImages","noAltTextGenerated","xOfYImagesUpdated","altTextDescription","altTextRequirement1","altTextRequirement2","altTextRequirement3","pleaseSaveDocumentFirst","unsupportedMimeType","theAlternateTextIsRequired","altTextHealthDescription","altTextHealthWidget","collectionCheckFailed","healthCheckPartialWarning","localeCount","noImagesFound","statusHealthy","statusUnhealthy","totalImageCount"],"mappings":"AAEA,OAAO,MAAMA,KAAgC;IAC3CC,SAAS;IACT,yCAAyC;QACvC,eAAe;QACfC,eAAe;QACfC,UAAU;QACVC,qBAAqB;QAErB,gBAAgB;QAChBC,iBAAiB;QACjBC,wBAAwB;QACxBC,0BAA0B;QAE1B,iBAAiB;QACjBC,yBACE;QACFC,6BAA6B;QAC7BC,wBAAwB;QACxBC,kBAAkB;QAClBC,4BAA4B;QAC5BC,oBAAoB;QACpBC,mBAAmB;QAEnB,YAAY;QACZC,oBACE;QACFC,qBAAqB;QACrBC,qBACE;QACFC,qBACE;QAEF,WAAW;QACXC,yBAAyB;QACzBC,qBAAqB;QAErB,sBAAsB;QACtBC,4BAA4B;QAE5B,mBAAmB;QACnBC,0BAA0B;QAC1BC,qBAAqB;QACrBC,uBAAuB;QACvBC,2BAA2B;QAC3BC,aAAa;QACbC,eAAe;QACfC,eAAe;QACfC,iBAAiB;QACjBC,iBAAiB;IACnB;AACF,EAAC"}
|
|
@@ -19,10 +19,9 @@
|
|
|
19
19
|
"failedToGenerate": { "type": "string" },
|
|
20
20
|
"failedToGenerateForXImages": { "type": "string" },
|
|
21
21
|
"generateAltText": { "type": "string" },
|
|
22
|
-
"
|
|
22
|
+
"generateAltTextFor_one": { "type": "string" },
|
|
23
|
+
"generateAltTextFor_other": { "type": "string" },
|
|
23
24
|
"healthCheckPartialWarning": { "type": "string" },
|
|
24
|
-
"image": { "type": "string" },
|
|
25
|
-
"images": { "type": "string" },
|
|
26
25
|
"keywords": { "type": "string" },
|
|
27
26
|
"localeCount": { "type": "string" },
|
|
28
27
|
"keywordsDescription": { "type": "string" },
|
|
@@ -50,10 +49,9 @@
|
|
|
50
49
|
"failedToGenerate",
|
|
51
50
|
"failedToGenerateForXImages",
|
|
52
51
|
"generateAltText",
|
|
53
|
-
"
|
|
52
|
+
"generateAltTextFor_one",
|
|
53
|
+
"generateAltTextFor_other",
|
|
54
54
|
"healthCheckPartialWarning",
|
|
55
|
-
"image",
|
|
56
|
-
"images",
|
|
57
55
|
"keywords",
|
|
58
56
|
"keywordsDescription",
|
|
59
57
|
"localeCount",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Field, PayloadRequest } from 'payload';
|
|
2
2
|
import type { AltTextResolver } from '../resolvers/types.js';
|
|
3
|
+
import type { AltTextCollectionConfig, IncomingCollectionsConfig, NormalizedAltTextCollectionConfig } from '../utilities/mimeTypes.js';
|
|
4
|
+
export type { AltTextCollectionConfig, NormalizedAltTextCollectionConfig };
|
|
3
5
|
/** Configuration options for the alt text plugin. */
|
|
4
6
|
export type IncomingAltTextPluginConfig = {
|
|
5
7
|
/**
|
|
@@ -11,8 +13,22 @@ export type IncomingAltTextPluginConfig = {
|
|
|
11
13
|
access?: (args: {
|
|
12
14
|
req: PayloadRequest;
|
|
13
15
|
}) => boolean | Promise<boolean>;
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Collections to enable the plugin for.
|
|
18
|
+
*
|
|
19
|
+
* Each entry may be a bare collection slug or an object with a `slug` and an
|
|
20
|
+
* optional `mimeTypes` array restricting which MIME types are tracked,
|
|
21
|
+
* validated, and generated. Bare slugs default to `['image/*']`.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* collections: [
|
|
26
|
+
* 'images', // shorthand — defaults to ['image/*']
|
|
27
|
+
* { slug: 'media', mimeTypes: ['image/*', 'application/pdf'] },
|
|
28
|
+
* ]
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
collections: IncomingCollectionsConfig;
|
|
16
32
|
/** Whether the plugin is enabled. */
|
|
17
33
|
enabled?: boolean;
|
|
18
34
|
/** Override the default fields inserted by the plugin via a function that receives the default fields and returns the new fields */
|
|
@@ -57,8 +73,8 @@ export type AltTextPluginConfig = {
|
|
|
57
73
|
access: (args: {
|
|
58
74
|
req: PayloadRequest;
|
|
59
75
|
}) => boolean | Promise<boolean>;
|
|
60
|
-
/**
|
|
61
|
-
collections:
|
|
76
|
+
/** Collections with resolved MIME type filters. */
|
|
77
|
+
collections: NormalizedAltTextCollectionConfig[];
|
|
62
78
|
/** Whether the plugin is enabled. */
|
|
63
79
|
enabled: boolean;
|
|
64
80
|
/** Override the default fields inserted by the plugin via a function that receives the default fields and returns the new fields */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types/AltTextPluginConfig.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"sources":["../../src/types/AltTextPluginConfig.ts"],"sourcesContent":["import type { Field, PayloadRequest } from 'payload'\n\nimport type { AltTextResolver } from '../resolvers/types.js'\nimport type {\n AltTextCollectionConfig,\n IncomingCollectionsConfig,\n NormalizedAltTextCollectionConfig,\n} from '../utilities/mimeTypes.js'\n\nexport type { AltTextCollectionConfig, NormalizedAltTextCollectionConfig }\n\n/** Configuration options for the alt text plugin. */\nexport type IncomingAltTextPluginConfig = {\n /**\n * Custom access control for plugin endpoints.\n * Return `true` to allow access, `false` to deny.\n *\n * @default ({ req }) => !!req.user — requires authentication\n */\n access?: (args: { req: PayloadRequest }) => boolean | Promise<boolean>\n\n /**\n * Collections to enable the plugin for.\n *\n * Each entry may be a bare collection slug or an object with a `slug` and an\n * optional `mimeTypes` array restricting which MIME types are tracked,\n * validated, and generated. Bare slugs default to `['image/*']`.\n *\n * @example\n * ```typescript\n * collections: [\n * 'images', // shorthand — defaults to ['image/*']\n * { slug: 'media', mimeTypes: ['image/*', 'application/pdf'] },\n * ]\n * ```\n */\n collections: IncomingCollectionsConfig\n\n /** Whether the plugin is enabled. */\n enabled?: boolean\n\n /** Override the default fields inserted by the plugin via a function that receives the default fields and returns the new fields */\n fieldsOverride?: (args: { defaultFields: Field[] }) => Field[]\n\n /**\n * Function to get the thumbnail URL of an image document.\n * This URL will be sent to the LLM for analysis.\n *\n * @remarks\n * - The URL must be publicly accessible so the LLM can fetch it\n * - Use a thumbnail/preview version of the image when possible (e.g. from the sizes field)\n */\n getImageThumbnail: (doc: Record<string, unknown>) => string\n\n /**\n * Enable alt text health tracking (REST endpoint, cache revalidation hooks, and dashboard widget).\n * Set to `false` to disable the entire feature.\n *\n * @default true\n */\n healthCheck?: boolean\n\n /**\n * The locale to generate alt texts in when localization is disabled.\n *\n * Required when localization is disabled, ignored when localization is enabled.\n * @example 'en'\n */\n locale?: string\n\n /**\n * Maximum number of concurrent API requests for bulk generate operations.\n *\n * @default 16\n */\n maxBulkGenerateConcurrency?: number\n\n /** The resolver to use for generating alt text (e.g., openAIResolver) */\n resolver: AltTextResolver\n}\n\n/** Configuration of the alt text plugin after defaults have been applied. */\nexport type AltTextPluginConfig = {\n /** Access control for plugin endpoints. */\n access: (args: { req: PayloadRequest }) => boolean | Promise<boolean>\n\n /** Collections with resolved MIME type filters. */\n collections: NormalizedAltTextCollectionConfig[]\n\n /** Whether the plugin is enabled. */\n enabled: boolean\n\n /** Override the default fields inserted by the plugin via a function that receives the default fields and returns the new fields */\n fieldsOverride?: (args: { defaultFields: Field[] }) => Field[]\n\n /** Function to get the thumbnail URL of an image document. */\n getImageThumbnail: (doc: Record<string, unknown>) => string\n\n /** Whether alt text health tracking is enabled. */\n healthCheck: boolean\n\n /** The locale to generate alt texts in when localization is disabled. */\n locale?: string\n\n /** The locales to generate alt texts for. */\n locales: string[]\n\n /** Maximum number of concurrent API requests for bulk generate operations. */\n maxBulkGenerateConcurrency: number\n\n /** The resolver to use for generating alt text */\n resolver: AltTextResolver\n}\n"],"names":[],"mappings":"AAiFA,2EAA2E,GAC3E,WA8BC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { unstable_cache } from 'next/cache.js';
|
|
2
2
|
import { createCachedAltTextHealthScan } from './altTextHealthCache.js';
|
|
3
3
|
import { localesFromConfig } from './localesFromConfig.js';
|
|
4
|
+
import { buildMimeTypeWhere } from './mimeTypes.js';
|
|
4
5
|
import { summarizeCollection } from './summarizeCollection.js';
|
|
5
6
|
export const ALT_TEXT_HEALTH_PLUGIN_SLUG = 'alt-text';
|
|
6
7
|
export const ALT_TEXT_HEALTH_CACHE_TTL = 3600;
|
|
@@ -21,7 +22,11 @@ const createCollectionReadError = (collection, message)=>({
|
|
|
21
22
|
operation: 'find'
|
|
22
23
|
});
|
|
23
24
|
const PAGE_SIZE = 500;
|
|
24
|
-
async function fetchAllDocs(payload, collection, isLocalized) {
|
|
25
|
+
async function fetchAllDocs(payload, collection, isLocalized, mimeTypes) {
|
|
26
|
+
const where = buildMimeTypeWhere(mimeTypes);
|
|
27
|
+
if (!where) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
25
30
|
const docs = [];
|
|
26
31
|
let page = 1;
|
|
27
32
|
let hasMore = true;
|
|
@@ -36,7 +41,8 @@ async function fetchAllDocs(payload, collection, isLocalized) {
|
|
|
36
41
|
page,
|
|
37
42
|
select: {
|
|
38
43
|
alt: true
|
|
39
|
-
}
|
|
44
|
+
},
|
|
45
|
+
where
|
|
40
46
|
});
|
|
41
47
|
for (const doc of result.docs){
|
|
42
48
|
docs.push({
|
|
@@ -50,27 +56,27 @@ async function fetchAllDocs(payload, collection, isLocalized) {
|
|
|
50
56
|
return docs;
|
|
51
57
|
}
|
|
52
58
|
async function computeAltTextHealthScan({ collections, isLocalized, localeCodes, payload }) {
|
|
53
|
-
const collectionSummaries = await Promise.all(collections.map(async (
|
|
59
|
+
const collectionSummaries = await Promise.all(collections.map(async ({ slug, mimeTypes })=>{
|
|
54
60
|
try {
|
|
55
|
-
const docs = await fetchAllDocs(payload,
|
|
61
|
+
const docs = await fetchAllDocs(payload, slug, isLocalized, mimeTypes);
|
|
56
62
|
return summarizeCollection({
|
|
57
|
-
collection,
|
|
63
|
+
collection: slug,
|
|
58
64
|
docs,
|
|
59
65
|
isLocalized,
|
|
60
66
|
localeCodes
|
|
61
67
|
});
|
|
62
68
|
} catch (error) {
|
|
63
69
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
64
|
-
const collectionError = createCollectionReadError(
|
|
70
|
+
const collectionError = createCollectionReadError(slug, message);
|
|
65
71
|
payload.logger.error({
|
|
66
|
-
collection,
|
|
72
|
+
collection: slug,
|
|
67
73
|
err: error,
|
|
68
74
|
msg: 'Alt text health check failed while reading a collection.',
|
|
69
75
|
operation: 'find',
|
|
70
76
|
plugin: ALT_TEXT_HEALTH_PLUGIN_SLUG
|
|
71
77
|
});
|
|
72
78
|
return {
|
|
73
|
-
collection,
|
|
79
|
+
collection: slug,
|
|
74
80
|
completeDocs: 0,
|
|
75
81
|
error: collectionError,
|
|
76
82
|
invalidDocIds: undefined,
|
|
@@ -112,12 +118,14 @@ async function getAltTextHealthScan(req) {
|
|
|
112
118
|
ALT_TEXT_HEALTH_GLOBAL_TAG,
|
|
113
119
|
[
|
|
114
120
|
...collections
|
|
115
|
-
].
|
|
121
|
+
].map(({ slug, mimeTypes })=>`${slug}:${[
|
|
122
|
+
...mimeTypes
|
|
123
|
+
].sort().join('|')}`).sort().join(','),
|
|
116
124
|
localeCodes.join(',')
|
|
117
125
|
];
|
|
118
126
|
const tags = [
|
|
119
127
|
ALT_TEXT_HEALTH_GLOBAL_TAG,
|
|
120
|
-
...new Set(collections.map((
|
|
128
|
+
...new Set(collections.map(({ slug })=>getAltTextHealthCollectionTag(slug)))
|
|
121
129
|
];
|
|
122
130
|
const getCachedHealthScan = createCachedAltTextHealthScan({
|
|
123
131
|
cacheFactory: unstable_cache,
|