@pigment/auto-translate 1.3.1 → 1.3.2

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
@@ -8,6 +8,7 @@ A powerful auto-translation plugin for [Payload CMS](https://payloadcms.com) tha
8
8
  - 🔒 **Field-Level Exclusion**: Toggle "do not translate" on specific fields in secondary languages
9
9
  - 🎯 **Smart Translation**: Preserves excluded fields when updating default language content
10
10
  - 🔧 **Flexible Configuration**: Configure per-collection settings and global exclusions
11
+ - 🔐 **Protected Settings**: Lock/unlock translation settings to prevent accidental changes
11
12
  - 🤖 **OpenAI Integration**: Uses GPT-4o for high-quality translations (with custom provider support)
12
13
  - 📦 **Zero UI Overhead**: Seamlessly integrates with Payload's admin panel
13
14
  - ⚡ **Performance Optimized**: 10-15x faster translation with smart extraction and deduplication
@@ -357,6 +358,19 @@ pnpm test:e2e # Run end-to-end tests
357
358
 
358
359
  ---
359
360
 
361
+ ## 🛠️ Development
362
+
363
+ The development server automatically seeds the database on first run with test data:
364
+ - Creates a dev user (if not exists)
365
+ - Creates test posts in the default language
366
+ - **Only seeds when database is empty** - subsequent restarts skip seeding
367
+
368
+ To re-seed the database:
369
+ 1. Delete all posts from the admin panel
370
+ 2. Restart dev server - auto-seeding will run
371
+
372
+ ---
373
+
360
374
  ## 🔒 Access Control
361
375
 
362
376
  The plugin respects Payload's access control. Translation operations run with the same permissions as the user making the update.
@@ -374,6 +388,19 @@ If you need custom access control for translation features, you can add hooks or
374
388
 
375
389
  ---
376
390
 
391
+ ## 📖 Additional Documentation
392
+
393
+ For more detailed information, check out these guides:
394
+
395
+ - **[QUICKSTART.md](./docs/QUICKSTART.md)** - Quick setup and basic usage
396
+ - **[USAGE_GUIDE.md](./docs/USAGE_GUIDE.md)** - Detailed usage examples and best practices
397
+ - **[ARCHITECTURE.md](./docs/ARCHITECTURE.md)** - Technical architecture and implementation details
398
+ - **[INTEGRATION_EXAMPLES.md](./docs/INTEGRATION_EXAMPLES.md)** - Real-world integration examples
399
+ - **[TRANSLATION_SETTINGS_LOCK.md](./docs/TRANSLATION_SETTINGS_LOCK.md)** - Lock/unlock feature for translation settings
400
+ - **[FEATURE_SUMMARY.md](./docs/FEATURE_SUMMARY.md)** - Complete feature overview
401
+
402
+ ---
403
+
377
404
  ## 🤝 Contributing
378
405
 
379
406
  Contributions are welcome! Please feel free to submit a Pull Request.
@@ -8,18 +8,18 @@ export const getTranslationExclusionsCollection = (slug = 'translation-exclusion
8
8
  },
9
9
  admin: {
10
10
  defaultColumns: [
11
- 'collection',
11
+ 'collectionSlug',
12
12
  'documentId',
13
13
  'locale',
14
14
  'excludedPaths'
15
15
  ],
16
- description: 'Stores field-level translation exclusions per document and locale. Each locale can have its own set of excluded fields. There should only be ONE record per (collection, documentId, locale) combination.',
16
+ description: 'Stores field-level translation exclusions per document and locale. Each locale can have its own set of excluded fields. There should only be ONE record per (collectionSlug, documentId, locale) combination.',
17
17
  group: 'Settings',
18
- useAsTitle: 'collection'
18
+ useAsTitle: 'collectionSlug'
19
19
  },
20
20
  fields: [
21
21
  {
22
- name: 'collection',
22
+ name: 'collectionSlug',
23
23
  type: 'text',
24
24
  admin: {
25
25
  description: 'The collection this exclusion belongs to',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collections/translationExclusions.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nexport const getTranslationExclusionsCollection = (\n slug: string = 'translation-exclusions',\n): CollectionConfig => ({\n slug,\n access: {\n create: () => true,\n delete: () => true,\n read: () => true,\n update: () => true,\n },\n admin: {\n defaultColumns: ['collection', 'documentId', 'locale', 'excludedPaths'],\n description:\n 'Stores field-level translation exclusions per document and locale. Each locale can have its own set of excluded fields. There should only be ONE record per (collection, documentId, locale) combination.',\n group: 'Settings',\n useAsTitle: 'collection',\n },\n fields: [\n {\n name: 'collection',\n type: 'text',\n admin: {\n description: 'The collection this exclusion belongs to',\n position: 'sidebar',\n readOnly: true,\n },\n index: true,\n required: true,\n },\n {\n name: 'documentId',\n type: 'text',\n admin: {\n description: 'The ID of the document',\n position: 'sidebar',\n readOnly: true,\n },\n index: true,\n required: true,\n },\n {\n name: 'locale',\n type: 'text',\n admin: {\n description: 'The locale these exclusions apply to (e.g., \"en\", \"de\", \"fr\")',\n position: 'sidebar',\n readOnly: true,\n },\n index: true,\n label: 'Locale',\n required: true,\n },\n {\n name: 'excludedPaths',\n type: 'array',\n admin: {\n description:\n 'Fields that should NOT be auto-translated in this specific locale. Each locale has its own independent set of exclusions.',\n },\n fields: [\n {\n name: 'path',\n type: 'text',\n admin: {\n description: 'Field path (e.g., \"title\", \"content.0.description\")',\n },\n label: 'Field Path',\n required: true,\n },\n ],\n label: `Excluded Fields for this Locale`,\n required: true,\n },\n ],\n})\n"],"names":["getTranslationExclusionsCollection","slug","access","create","delete","read","update","admin","defaultColumns","description","group","useAsTitle","fields","name","type","position","readOnly","index","required","label"],"mappings":"AAEA,OAAO,MAAMA,qCAAqC,CAChDC,OAAe,wBAAwB,GACjB,CAAA;QACtBA;QACAC,QAAQ;YACNC,QAAQ,IAAM;YACdC,QAAQ,IAAM;YACdC,MAAM,IAAM;YACZC,QAAQ,IAAM;QAChB;QACAC,OAAO;YACLC,gBAAgB;gBAAC;gBAAc;gBAAc;gBAAU;aAAgB;YACvEC,aACE;YACFC,OAAO;YACPC,YAAY;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLE,aAAa;oBACbM,UAAU;oBACVC,UAAU;gBACZ;gBACAC,OAAO;gBACPC,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLE,aAAa;oBACbM,UAAU;oBACVC,UAAU;gBACZ;gBACAC,OAAO;gBACPC,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLE,aAAa;oBACbM,UAAU;oBACVC,UAAU;gBACZ;gBACAC,OAAO;gBACPE,OAAO;gBACPD,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLE,aACE;gBACJ;gBACAG,QAAQ;oBACN;wBACEC,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLE,aAAa;wBACf;wBACAU,OAAO;wBACPD,UAAU;oBACZ;iBACD;gBACDC,OAAO,CAAC,+BAA+B,CAAC;gBACxCD,UAAU;YACZ;SACD;IACH,CAAA,EAAE"}
1
+ {"version":3,"sources":["../../src/collections/translationExclusions.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nexport const getTranslationExclusionsCollection = (\n slug: string = 'translation-exclusions',\n): CollectionConfig => ({\n slug,\n access: {\n create: () => true,\n delete: () => true,\n read: () => true,\n update: () => true,\n },\n admin: {\n defaultColumns: ['collectionSlug', 'documentId', 'locale', 'excludedPaths'],\n description:\n 'Stores field-level translation exclusions per document and locale. Each locale can have its own set of excluded fields. There should only be ONE record per (collectionSlug, documentId, locale) combination.',\n group: 'Settings',\n useAsTitle: 'collectionSlug',\n },\n fields: [\n {\n name: 'collectionSlug',\n type: 'text',\n admin: {\n description: 'The collection this exclusion belongs to',\n position: 'sidebar',\n readOnly: true,\n },\n index: true,\n required: true,\n },\n {\n name: 'documentId',\n type: 'text',\n admin: {\n description: 'The ID of the document',\n position: 'sidebar',\n readOnly: true,\n },\n index: true,\n required: true,\n },\n {\n name: 'locale',\n type: 'text',\n admin: {\n description: 'The locale these exclusions apply to (e.g., \"en\", \"de\", \"fr\")',\n position: 'sidebar',\n readOnly: true,\n },\n index: true,\n label: 'Locale',\n required: true,\n },\n {\n name: 'excludedPaths',\n type: 'array',\n admin: {\n description:\n 'Fields that should NOT be auto-translated in this specific locale. Each locale has its own independent set of exclusions.',\n },\n fields: [\n {\n name: 'path',\n type: 'text',\n admin: {\n description: 'Field path (e.g., \"title\", \"content.0.description\")',\n },\n label: 'Field Path',\n required: true,\n },\n ],\n label: `Excluded Fields for this Locale`,\n required: true,\n },\n ],\n})\n"],"names":["getTranslationExclusionsCollection","slug","access","create","delete","read","update","admin","defaultColumns","description","group","useAsTitle","fields","name","type","position","readOnly","index","required","label"],"mappings":"AAEA,OAAO,MAAMA,qCAAqC,CAChDC,OAAe,wBAAwB,GACjB,CAAA;QACtBA;QACAC,QAAQ;YACNC,QAAQ,IAAM;YACdC,QAAQ,IAAM;YACdC,MAAM,IAAM;YACZC,QAAQ,IAAM;QAChB;QACAC,OAAO;YACLC,gBAAgB;gBAAC;gBAAkB;gBAAc;gBAAU;aAAgB;YAC3EC,aACE;YACFC,OAAO;YACPC,YAAY;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLE,aAAa;oBACbM,UAAU;oBACVC,UAAU;gBACZ;gBACAC,OAAO;gBACPC,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLE,aAAa;oBACbM,UAAU;oBACVC,UAAU;gBACZ;gBACAC,OAAO;gBACPC,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLE,aAAa;oBACbM,UAAU;oBACVC,UAAU;gBACZ;gBACAC,OAAO;gBACPE,OAAO;gBACPD,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLE,aACE;gBACJ;gBACAG,QAAQ;oBACN;wBACEC,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLE,aAAa;wBACf;wBACAU,OAAO;wBACPD,UAAU;oBACZ;iBACD;gBACDC,OAAO,CAAC,+BAA+B,CAAC;gBACxCD,UAAU;YACZ;SACD;IACH,CAAA,EAAE"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Client-side function to update the lockTranslationSettings field
3
+ * Makes a fetch request to update the global
4
+ */
5
+ export declare function updateLockTranslationSettingsField(isLocked: boolean): Promise<{
6
+ isLocked: boolean;
7
+ success: boolean;
8
+ }>;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Client-side function to update the lockTranslationSettings field
3
+ * Makes a fetch request to update the global
4
+ */ export async function updateLockTranslationSettingsField(isLocked) {
5
+ try {
6
+ const response = await fetch('/api/globals/translation-settings', {
7
+ body: JSON.stringify({
8
+ lockTranslationSettings: isLocked
9
+ }),
10
+ credentials: 'include',
11
+ headers: {
12
+ 'Content-Type': 'application/json'
13
+ },
14
+ method: 'POST'
15
+ });
16
+ if (!response.ok) {
17
+ throw new Error(`Failed to update: ${response.statusText}`);
18
+ }
19
+ return {
20
+ isLocked,
21
+ success: true
22
+ };
23
+ } catch (error) {
24
+ // eslint-disable-next-line no-console
25
+ console.error('[LockTranslation] Error updating lock state:', error);
26
+ return {
27
+ isLocked: !isLocked,
28
+ success: false
29
+ };
30
+ }
31
+ }
32
+
33
+ //# sourceMappingURL=lockTranslations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/LockTranslation/actions/lockTranslations.ts"],"sourcesContent":["/**\n * Client-side function to update the lockTranslationSettings field\n * Makes a fetch request to update the global\n */\nexport async function updateLockTranslationSettingsField(\n isLocked: boolean,\n): Promise<{ isLocked: boolean; success: boolean }> {\n try {\n const response = await fetch('/api/globals/translation-settings', {\n body: JSON.stringify({\n lockTranslationSettings: isLocked,\n }),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (!response.ok) {\n throw new Error(`Failed to update: ${response.statusText}`)\n }\n\n return {\n isLocked,\n success: true,\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[LockTranslation] Error updating lock state:', error)\n return {\n isLocked: !isLocked,\n success: false,\n }\n }\n}\n"],"names":["updateLockTranslationSettingsField","isLocked","response","fetch","body","JSON","stringify","lockTranslationSettings","credentials","headers","method","ok","Error","statusText","success","error","console"],"mappings":"AAAA;;;CAGC,GACD,OAAO,eAAeA,mCACpBC,QAAiB;IAEjB,IAAI;QACF,MAAMC,WAAW,MAAMC,MAAM,qCAAqC;YAChEC,MAAMC,KAAKC,SAAS,CAAC;gBACnBC,yBAAyBN;YAC3B;YACAO,aAAa;YACbC,SAAS;gBACP,gBAAgB;YAClB;YACAC,QAAQ;QACV;QAEA,IAAI,CAACR,SAASS,EAAE,EAAE;YAChB,MAAM,IAAIC,MAAM,CAAC,kBAAkB,EAAEV,SAASW,UAAU,EAAE;QAC5D;QAEA,OAAO;YACLZ;YACAa,SAAS;QACX;IACF,EAAE,OAAOC,OAAO;QACd,sCAAsC;QACtCC,QAAQD,KAAK,CAAC,gDAAgDA;QAC9D,OAAO;YACLd,UAAU,CAACA;YACXa,SAAS;QACX;IACF;AACF"}
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import './style.css';
3
+ /**
4
+ * Component that provides lock/unlock functionality for translation settings
5
+ * - Fields are locked by default (read-only)
6
+ * - User must click "Unlock" to edit
7
+ * - After saving, fields automatically lock again
8
+ * - Updates the hidden lockTranslationSettings checkbox field to enable server-side access control
9
+ */
10
+ export declare const LockTranslation: React.FC;
@@ -0,0 +1,98 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useForm, useFormFields } from '@payloadcms/ui';
4
+ import React, { useCallback, useEffect, useState } from 'react';
5
+ import { updateLockTranslationSettingsField } from './actions/lockTranslations.js';
6
+ import './style.css';
7
+ /**
8
+ * Component that provides lock/unlock functionality for translation settings
9
+ * - Fields are locked by default (read-only)
10
+ * - User must click "Unlock" to edit
11
+ * - After saving, fields automatically lock again
12
+ * - Updates the hidden lockTranslationSettings checkbox field to enable server-side access control
13
+ */ export const LockTranslation = ()=>{
14
+ const lockField = useFormFields(([fields])=>fields?.lockTranslationSettings);
15
+ const { dispatchFields } = useForm();
16
+ const isLocked = Boolean(lockField?.value ?? true);
17
+ const [isLoading, setIsLoading] = useState(false);
18
+ // Apply lock state to fields by directly manipulating their disabled state
19
+ const applyLockStateToFields = useCallback((locked)=>{
20
+ const fieldsToLock = [
21
+ 'systemPrompt',
22
+ 'translationRules',
23
+ 'model',
24
+ 'temperature',
25
+ 'maxTokens'
26
+ ];
27
+ fieldsToLock.forEach((fieldPath)=>{
28
+ // Find the input/textarea elements for this field
29
+ const inputs = document.querySelectorAll(`[name="${fieldPath}"], textarea[id*="${fieldPath}"], input[id*="${fieldPath}"]`);
30
+ inputs.forEach((input)=>{
31
+ if (locked) {
32
+ input.setAttribute('disabled', 'true');
33
+ } else {
34
+ input.removeAttribute('disabled');
35
+ }
36
+ });
37
+ });
38
+ }, []);
39
+ // Apply initial lock state when component mounts or lock state changes
40
+ useEffect(()=>{
41
+ // Small delay to ensure form fields are rendered
42
+ const timer = setTimeout(()=>{
43
+ applyLockStateToFields(isLocked);
44
+ }, 100);
45
+ return ()=>clearTimeout(timer);
46
+ }, [
47
+ isLocked,
48
+ applyLockStateToFields
49
+ ]);
50
+ const toggleLock = useCallback(async ()=>{
51
+ const newLockState = !isLocked;
52
+ setIsLoading(true);
53
+ try {
54
+ const result = await updateLockTranslationSettingsField(newLockState);
55
+ if (result.success) {
56
+ // Update the lockTranslationSettings field value
57
+ dispatchFields({
58
+ type: 'UPDATE',
59
+ path: 'lockTranslationSettings',
60
+ value: result.isLocked
61
+ });
62
+ // Apply lock state to fields
63
+ applyLockStateToFields(result.isLocked);
64
+ }
65
+ } catch (error) {
66
+ // eslint-disable-next-line no-console
67
+ console.error('[LockTranslation] Error:', error);
68
+ } finally{
69
+ setIsLoading(false);
70
+ }
71
+ }, [
72
+ isLocked,
73
+ dispatchFields,
74
+ applyLockStateToFields
75
+ ]);
76
+ return /*#__PURE__*/ _jsx("div", {
77
+ className: "translation-settings-lock-container",
78
+ children: /*#__PURE__*/ _jsxs("button", {
79
+ className: `translation-settings-lock-button ${isLocked ? 'locked' : 'unlocked'}`,
80
+ disabled: isLoading,
81
+ onClick: toggleLock,
82
+ title: isLocked ? '🔒 Settings are locked to prevent accidental changes. Click to unlock and edit.' : '🔓 Settings are unlocked. Click to lock after saving your changes.',
83
+ type: "button",
84
+ children: [
85
+ /*#__PURE__*/ _jsx("span", {
86
+ className: "translation-settings-lock-icon",
87
+ children: isLoading ? '⏳' : isLocked ? '🔒' : '🔓'
88
+ }),
89
+ /*#__PURE__*/ _jsx("span", {
90
+ className: "translation-settings-lock-label",
91
+ children: isLoading ? 'Updating...' : isLocked ? 'Unlock Settings' : 'Lock Settings'
92
+ })
93
+ ]
94
+ })
95
+ });
96
+ };
97
+
98
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/LockTranslation/index.tsx"],"sourcesContent":["'use client'\n\nimport { useForm, useFormFields } from '@payloadcms/ui'\nimport React, { useCallback, useEffect, useState } from 'react'\n\nimport { updateLockTranslationSettingsField } from './actions/lockTranslations.js'\nimport './style.css'\n\n/**\n * Component that provides lock/unlock functionality for translation settings\n * - Fields are locked by default (read-only)\n * - User must click \"Unlock\" to edit\n * - After saving, fields automatically lock again\n * - Updates the hidden lockTranslationSettings checkbox field to enable server-side access control\n */\nexport const LockTranslation: React.FC = () => {\n const lockField = useFormFields(([fields]) => fields?.lockTranslationSettings)\n const { dispatchFields } = useForm()\n const isLocked = Boolean(lockField?.value ?? true)\n const [isLoading, setIsLoading] = useState(false)\n\n // Apply lock state to fields by directly manipulating their disabled state\n const applyLockStateToFields = useCallback((locked: boolean) => {\n const fieldsToLock = ['systemPrompt', 'translationRules', 'model', 'temperature', 'maxTokens']\n\n fieldsToLock.forEach((fieldPath) => {\n // Find the input/textarea elements for this field\n const inputs = document.querySelectorAll<HTMLInputElement | HTMLTextAreaElement>(\n `[name=\"${fieldPath}\"], textarea[id*=\"${fieldPath}\"], input[id*=\"${fieldPath}\"]`,\n )\n\n inputs.forEach((input) => {\n if (locked) {\n input.setAttribute('disabled', 'true')\n } else {\n input.removeAttribute('disabled')\n }\n })\n })\n }, [])\n\n // Apply initial lock state when component mounts or lock state changes\n useEffect(() => {\n // Small delay to ensure form fields are rendered\n const timer = setTimeout(() => {\n applyLockStateToFields(isLocked)\n }, 100)\n\n return () => clearTimeout(timer)\n }, [isLocked, applyLockStateToFields])\n\n const toggleLock = useCallback(async () => {\n const newLockState = !isLocked\n setIsLoading(true)\n\n try {\n const result = await updateLockTranslationSettingsField(newLockState)\n\n if (result.success) {\n // Update the lockTranslationSettings field value\n dispatchFields({\n type: 'UPDATE',\n path: 'lockTranslationSettings',\n value: result.isLocked,\n })\n\n // Apply lock state to fields\n applyLockStateToFields(result.isLocked)\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[LockTranslation] Error:', error)\n } finally {\n setIsLoading(false)\n }\n }, [isLocked, dispatchFields, applyLockStateToFields])\n\n return (\n <div className=\"translation-settings-lock-container\">\n <button\n className={`translation-settings-lock-button ${isLocked ? 'locked' : 'unlocked'}`}\n disabled={isLoading}\n onClick={toggleLock}\n title={\n isLocked\n ? '🔒 Settings are locked to prevent accidental changes. Click to unlock and edit.'\n : '🔓 Settings are unlocked. Click to lock after saving your changes.'\n }\n type=\"button\"\n >\n <span className=\"translation-settings-lock-icon\">\n {isLoading ? '⏳' : isLocked ? '🔒' : '🔓'}\n </span>\n <span className=\"translation-settings-lock-label\">\n {isLoading ? 'Updating...' : isLocked ? 'Unlock Settings' : 'Lock Settings'}\n </span>\n </button>\n </div>\n )\n}\n"],"names":["useForm","useFormFields","React","useCallback","useEffect","useState","updateLockTranslationSettingsField","LockTranslation","lockField","fields","lockTranslationSettings","dispatchFields","isLocked","Boolean","value","isLoading","setIsLoading","applyLockStateToFields","locked","fieldsToLock","forEach","fieldPath","inputs","document","querySelectorAll","input","setAttribute","removeAttribute","timer","setTimeout","clearTimeout","toggleLock","newLockState","result","success","type","path","error","console","div","className","button","disabled","onClick","title","span"],"mappings":"AAAA;;AAEA,SAASA,OAAO,EAAEC,aAAa,QAAQ,iBAAgB;AACvD,OAAOC,SAASC,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAE/D,SAASC,kCAAkC,QAAQ,gCAA+B;AAClF,OAAO,cAAa;AAEpB;;;;;;CAMC,GACD,OAAO,MAAMC,kBAA4B;IACvC,MAAMC,YAAYP,cAAc,CAAC,CAACQ,OAAO,GAAKA,QAAQC;IACtD,MAAM,EAAEC,cAAc,EAAE,GAAGX;IAC3B,MAAMY,WAAWC,QAAQL,WAAWM,SAAS;IAC7C,MAAM,CAACC,WAAWC,aAAa,GAAGX,SAAS;IAE3C,2EAA2E;IAC3E,MAAMY,yBAAyBd,YAAY,CAACe;QAC1C,MAAMC,eAAe;YAAC;YAAgB;YAAoB;YAAS;YAAe;SAAY;QAE9FA,aAAaC,OAAO,CAAC,CAACC;YACpB,kDAAkD;YAClD,MAAMC,SAASC,SAASC,gBAAgB,CACtC,CAAC,OAAO,EAAEH,UAAU,kBAAkB,EAAEA,UAAU,eAAe,EAAEA,UAAU,EAAE,CAAC;YAGlFC,OAAOF,OAAO,CAAC,CAACK;gBACd,IAAIP,QAAQ;oBACVO,MAAMC,YAAY,CAAC,YAAY;gBACjC,OAAO;oBACLD,MAAME,eAAe,CAAC;gBACxB;YACF;QACF;IACF,GAAG,EAAE;IAEL,uEAAuE;IACvEvB,UAAU;QACR,iDAAiD;QACjD,MAAMwB,QAAQC,WAAW;YACvBZ,uBAAuBL;QACzB,GAAG;QAEH,OAAO,IAAMkB,aAAaF;IAC5B,GAAG;QAAChB;QAAUK;KAAuB;IAErC,MAAMc,aAAa5B,YAAY;QAC7B,MAAM6B,eAAe,CAACpB;QACtBI,aAAa;QAEb,IAAI;YACF,MAAMiB,SAAS,MAAM3B,mCAAmC0B;YAExD,IAAIC,OAAOC,OAAO,EAAE;gBAClB,iDAAiD;gBACjDvB,eAAe;oBACbwB,MAAM;oBACNC,MAAM;oBACNtB,OAAOmB,OAAOrB,QAAQ;gBACxB;gBAEA,6BAA6B;gBAC7BK,uBAAuBgB,OAAOrB,QAAQ;YACxC;QACF,EAAE,OAAOyB,OAAO;YACd,sCAAsC;YACtCC,QAAQD,KAAK,CAAC,4BAA4BA;QAC5C,SAAU;YACRrB,aAAa;QACf;IACF,GAAG;QAACJ;QAAUD;QAAgBM;KAAuB;IAErD,qBACE,KAACsB;QAAIC,WAAU;kBACb,cAAA,MAACC;YACCD,WAAW,CAAC,iCAAiC,EAAE5B,WAAW,WAAW,YAAY;YACjF8B,UAAU3B;YACV4B,SAASZ;YACTa,OACEhC,WACI,oFACA;YAENuB,MAAK;;8BAEL,KAACU;oBAAKL,WAAU;8BACbzB,YAAY,MAAMH,WAAW,OAAO;;8BAEvC,KAACiC;oBAAKL,WAAU;8BACbzB,YAAY,gBAAgBH,WAAW,oBAAoB;;;;;AAKtE,EAAC"}
@@ -0,0 +1,87 @@
1
+ .translation-settings-lock-container {
2
+ display: inline-flex;
3
+ }
4
+
5
+ .translation-settings-lock-button {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ gap: 0.5rem;
9
+ padding: 0.5rem 1rem;
10
+ border: 1.5px solid var(--theme-elevation-400);
11
+ border-radius: var(--border-radius-s, 4px);
12
+ background: var(--theme-elevation-0);
13
+ cursor: pointer;
14
+ font-size: 13px;
15
+ font-weight: 600;
16
+ transition: all 0.2s ease;
17
+ white-space: nowrap;
18
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
19
+ outline: none;
20
+ }
21
+
22
+ .translation-settings-lock-button:hover {
23
+ background: var(--theme-elevation-100);
24
+ border-color: var(--theme-elevation-500);
25
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
26
+ }
27
+
28
+ .translation-settings-lock-button.locked {
29
+ background: var(--theme-error-50);
30
+ border-color: var(--theme-error-500);
31
+ color: var(--theme-error-800);
32
+ }
33
+
34
+ .translation-settings-lock-button.locked:hover {
35
+ background: var(--theme-error-100);
36
+ border-color: var(--theme-error-600);
37
+ transform: translateY(-1px);
38
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.12);
39
+ }
40
+
41
+ .translation-settings-lock-button.unlocked {
42
+ background: var(--theme-success-50);
43
+ border-color: var(--theme-success-500);
44
+ color: var(--theme-success-800);
45
+ }
46
+
47
+ .translation-settings-lock-button.unlocked:hover {
48
+ background: var(--theme-success-100);
49
+ border-color: var(--theme-success-600);
50
+ transform: translateY(-1px);
51
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.12);
52
+ }
53
+
54
+ .translation-settings-lock-icon {
55
+ font-size: 16px;
56
+ line-height: 1;
57
+ }
58
+
59
+ .translation-settings-lock-label {
60
+ font-weight: 600;
61
+ letter-spacing: 0.01em;
62
+ }
63
+
64
+ /* Style for locked input fields */
65
+ .translation-settings-locked {
66
+ background-color: var(--theme-elevation-50) !important;
67
+ cursor: not-allowed !important;
68
+ opacity: 0.7;
69
+ }
70
+
71
+ /* Style for locked save button */
72
+ .translation-settings-save-locked {
73
+ opacity: 0.5;
74
+ cursor: not-allowed !important;
75
+ }
76
+
77
+ /* Responsive adjustments */
78
+ @media (max-width: 768px) {
79
+ .translation-settings-lock-button {
80
+ font-size: 12px;
81
+ padding: 0.4rem 0.75rem;
82
+ }
83
+
84
+ .translation-settings-lock-icon {
85
+ font-size: 14px;
86
+ }
87
+ }
@@ -0,0 +1,87 @@
1
+ .translation-settings-lock-container {
2
+ display: inline-flex;
3
+ }
4
+
5
+ .translation-settings-lock-button {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ gap: 0.5rem;
9
+ padding: 0.5rem 1rem;
10
+ border: 1.5px solid var(--theme-elevation-400);
11
+ border-radius: var(--border-radius-s, 4px);
12
+ background: var(--theme-elevation-0);
13
+ cursor: pointer;
14
+ font-size: 13px;
15
+ font-weight: 600;
16
+ transition: all 0.2s ease;
17
+ white-space: nowrap;
18
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
19
+ outline: none;
20
+ }
21
+
22
+ .translation-settings-lock-button:hover {
23
+ background: var(--theme-elevation-100);
24
+ border-color: var(--theme-elevation-500);
25
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
26
+ }
27
+
28
+ .translation-settings-lock-button.locked {
29
+ background: var(--theme-error-50);
30
+ border-color: var(--theme-error-500);
31
+ color: var(--theme-error-800);
32
+ }
33
+
34
+ .translation-settings-lock-button.locked:hover {
35
+ background: var(--theme-error-100);
36
+ border-color: var(--theme-error-600);
37
+ transform: translateY(-1px);
38
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.12);
39
+ }
40
+
41
+ .translation-settings-lock-button.unlocked {
42
+ background: var(--theme-success-50);
43
+ border-color: var(--theme-success-500);
44
+ color: var(--theme-success-800);
45
+ }
46
+
47
+ .translation-settings-lock-button.unlocked:hover {
48
+ background: var(--theme-success-100);
49
+ border-color: var(--theme-success-600);
50
+ transform: translateY(-1px);
51
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.12);
52
+ }
53
+
54
+ .translation-settings-lock-icon {
55
+ font-size: 16px;
56
+ line-height: 1;
57
+ }
58
+
59
+ .translation-settings-lock-label {
60
+ font-weight: 600;
61
+ letter-spacing: 0.01em;
62
+ }
63
+
64
+ /* Style for locked input fields */
65
+ .translation-settings-locked {
66
+ background-color: var(--theme-elevation-50) !important;
67
+ cursor: not-allowed !important;
68
+ opacity: 0.7;
69
+ }
70
+
71
+ /* Style for locked save button */
72
+ .translation-settings-save-locked {
73
+ opacity: 0.5;
74
+ cursor: not-allowed !important;
75
+ }
76
+
77
+ /* Responsive adjustments */
78
+ @media (max-width: 768px) {
79
+ .translation-settings-lock-button {
80
+ font-size: 12px;
81
+ padding: 0.4rem 0.75rem;
82
+ }
83
+
84
+ .translation-settings-lock-icon {
85
+ font-size: 14px;
86
+ }
87
+ }
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import './TranslationSettingsLock.css';
3
+ /**
4
+ * Component that provides lock/unlock functionality for translation settings
5
+ * - Fields are locked by default (read-only)
6
+ * - User must click "Unlock" to edit
7
+ * - After saving, fields automatically lock again
8
+ */
9
+ export declare const TranslationSettingsLock: React.FC;
@@ -0,0 +1,155 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
4
+ import './TranslationSettingsLock.css';
5
+ /**
6
+ * Component that provides lock/unlock functionality for translation settings
7
+ * - Fields are locked by default (read-only)
8
+ * - User must click "Unlock" to edit
9
+ * - After saving, fields automatically lock again
10
+ */ export const TranslationSettingsLock = ()=>{
11
+ const [isLocked, setIsLocked] = useState(true);
12
+ const formRef = useRef(null);
13
+ const observerRef = useRef(null);
14
+ const debounceTimerRef = useRef(null);
15
+ // Find form element (memoized selector logic)
16
+ const findForm = useCallback(()=>{
17
+ if (formRef.current && document.contains(formRef.current)) {
18
+ return formRef.current;
19
+ }
20
+ const formElement = document.querySelector('form[id*="translation-settings"]') || document.querySelector('form[id*="Global"]') || document.querySelector('form.render-fields') || document.querySelector('form');
21
+ const form = formElement;
22
+ if (form) {
23
+ formRef.current = form;
24
+ }
25
+ return form;
26
+ }, []);
27
+ // Apply lock/unlock state to all form fields (optimized with caching)
28
+ const applyLockState = useCallback(()=>{
29
+ const form = findForm();
30
+ if (!form) {
31
+ return;
32
+ }
33
+ // Find all input fields - using more efficient selector
34
+ const inputs = form.querySelectorAll('input[type="text"], input[type="number"], textarea');
35
+ // Batch DOM updates for better performance
36
+ inputs.forEach((input)=>{
37
+ // Skip if this is the lock control itself
38
+ if (input.closest('.translation-settings-lock-container')) {
39
+ return;
40
+ }
41
+ // Use single operation to update multiple attributes
42
+ if (isLocked) {
43
+ input.setAttribute('readonly', 'true');
44
+ input.setAttribute('disabled', 'true');
45
+ input.classList.add('translation-settings-locked');
46
+ } else {
47
+ input.removeAttribute('readonly');
48
+ input.removeAttribute('disabled');
49
+ input.classList.remove('translation-settings-locked');
50
+ }
51
+ });
52
+ // Update the save button state
53
+ const saveButtonElement = document.querySelector('button#action-save');
54
+ const saveButton = saveButtonElement;
55
+ if (saveButton) {
56
+ if (isLocked) {
57
+ saveButton.setAttribute('disabled', 'true');
58
+ saveButton.classList.add('translation-settings-save-locked');
59
+ } else {
60
+ saveButton.removeAttribute('disabled');
61
+ saveButton.classList.remove('translation-settings-save-locked');
62
+ }
63
+ }
64
+ }, [
65
+ isLocked,
66
+ findForm
67
+ ]);
68
+ // Debounced version of applyLockState for MutationObserver
69
+ const debouncedApplyLockState = useCallback(()=>{
70
+ if (debounceTimerRef.current) {
71
+ clearTimeout(debounceTimerRef.current);
72
+ }
73
+ debounceTimerRef.current = setTimeout(()=>{
74
+ applyLockState();
75
+ }, 100); // 100ms debounce
76
+ }, [
77
+ applyLockState
78
+ ]);
79
+ // Setup MutationObserver for form changes
80
+ useEffect(()=>{
81
+ // Initial application
82
+ const initialTimeout = setTimeout(applyLockState, 100);
83
+ // Setup observer to watch for dynamic field additions
84
+ const form = findForm();
85
+ if (form) {
86
+ observerRef.current = new MutationObserver(debouncedApplyLockState);
87
+ // Only observe the form element, not the entire body (much more efficient)
88
+ observerRef.current.observe(form, {
89
+ attributes: false,
90
+ childList: true,
91
+ subtree: true
92
+ });
93
+ }
94
+ return ()=>{
95
+ clearTimeout(initialTimeout);
96
+ if (debounceTimerRef.current) {
97
+ clearTimeout(debounceTimerRef.current);
98
+ }
99
+ if (observerRef.current) {
100
+ observerRef.current.disconnect();
101
+ observerRef.current = null;
102
+ }
103
+ };
104
+ }, [
105
+ isLocked,
106
+ applyLockState,
107
+ debouncedApplyLockState,
108
+ findForm
109
+ ]);
110
+ // Listen for form submission to auto-lock after save
111
+ useEffect(()=>{
112
+ const form = findForm();
113
+ if (!form) {
114
+ return;
115
+ }
116
+ const handleSubmit = ()=>{
117
+ // Lock after a delay to ensure save completes
118
+ setTimeout(()=>{
119
+ setIsLocked(true);
120
+ }, 2000);
121
+ };
122
+ form.addEventListener('submit', handleSubmit);
123
+ return ()=>{
124
+ form.removeEventListener('submit', handleSubmit);
125
+ };
126
+ }, [
127
+ findForm
128
+ ]);
129
+ const toggleLock = useCallback(()=>{
130
+ setIsLocked((prev)=>!prev);
131
+ }, [
132
+ isLocked
133
+ ]);
134
+ return /*#__PURE__*/ _jsx("div", {
135
+ className: "translation-settings-lock-container",
136
+ children: /*#__PURE__*/ _jsxs("button", {
137
+ className: `translation-settings-lock-button ${isLocked ? 'locked' : 'unlocked'}`,
138
+ onClick: toggleLock,
139
+ title: isLocked ? '🔒 Settings are locked to prevent accidental changes. Click to unlock and edit.' : '🔓 Settings are unlocked. Click to lock after saving your changes.',
140
+ type: "button",
141
+ children: [
142
+ /*#__PURE__*/ _jsx("span", {
143
+ className: "translation-settings-lock-icon",
144
+ children: isLocked ? '🔒' : '🔓'
145
+ }),
146
+ /*#__PURE__*/ _jsx("span", {
147
+ className: "translation-settings-lock-label",
148
+ children: isLocked ? 'Unlock Settings' : 'Lock Settings'
149
+ })
150
+ ]
151
+ })
152
+ });
153
+ };
154
+
155
+ //# sourceMappingURL=TranslationSettingsLock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/TranslationSettingsLock.tsx"],"sourcesContent":["'use client'\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\n\nimport './TranslationSettingsLock.css'\n\n/**\n * Component that provides lock/unlock functionality for translation settings\n * - Fields are locked by default (read-only)\n * - User must click \"Unlock\" to edit\n * - After saving, fields automatically lock again\n */\nexport const TranslationSettingsLock: React.FC = () => {\n const [isLocked, setIsLocked] = useState(true)\n const formRef = useRef<HTMLFormElement | null>(null)\n const observerRef = useRef<MutationObserver | null>(null)\n const debounceTimerRef = useRef<NodeJS.Timeout | null>(null)\n\n // Find form element (memoized selector logic)\n const findForm = useCallback((): HTMLFormElement | null => {\n if (formRef.current && document.contains(formRef.current)) {\n return formRef.current\n }\n\n const formElement =\n document.querySelector('form[id*=\"translation-settings\"]') ||\n document.querySelector('form[id*=\"Global\"]') ||\n document.querySelector('form.render-fields') ||\n document.querySelector('form')\n\n const form = formElement as HTMLFormElement | null\n\n if (form) {\n formRef.current = form\n }\n\n return form\n }, [])\n\n // Apply lock/unlock state to all form fields (optimized with caching)\n const applyLockState = useCallback(() => {\n const form = findForm()\n\n if (!form) {\n return\n }\n\n // Find all input fields - using more efficient selector\n const inputs = form.querySelectorAll<HTMLInputElement | HTMLTextAreaElement>(\n 'input[type=\"text\"], input[type=\"number\"], textarea',\n )\n\n // Batch DOM updates for better performance\n inputs.forEach((input) => {\n // Skip if this is the lock control itself\n if (input.closest('.translation-settings-lock-container')) {\n return\n }\n\n // Use single operation to update multiple attributes\n if (isLocked) {\n input.setAttribute('readonly', 'true')\n input.setAttribute('disabled', 'true')\n input.classList.add('translation-settings-locked')\n } else {\n input.removeAttribute('readonly')\n input.removeAttribute('disabled')\n input.classList.remove('translation-settings-locked')\n }\n })\n\n // Update the save button state\n const saveButtonElement = document.querySelector('button#action-save')\n const saveButton = saveButtonElement as HTMLButtonElement | null\n\n if (saveButton) {\n if (isLocked) {\n saveButton.setAttribute('disabled', 'true')\n saveButton.classList.add('translation-settings-save-locked')\n } else {\n saveButton.removeAttribute('disabled')\n saveButton.classList.remove('translation-settings-save-locked')\n }\n }\n }, [isLocked, findForm])\n\n // Debounced version of applyLockState for MutationObserver\n const debouncedApplyLockState = useCallback(() => {\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current)\n }\n\n debounceTimerRef.current = setTimeout(() => {\n applyLockState()\n }, 100) // 100ms debounce\n }, [applyLockState])\n\n // Setup MutationObserver for form changes\n useEffect(() => {\n // Initial application\n const initialTimeout = setTimeout(applyLockState, 100)\n\n // Setup observer to watch for dynamic field additions\n const form = findForm()\n\n if (form) {\n observerRef.current = new MutationObserver(debouncedApplyLockState)\n\n // Only observe the form element, not the entire body (much more efficient)\n observerRef.current.observe(form, {\n attributes: false, // Don't watch attribute changes, only structure\n childList: true,\n subtree: true,\n })\n }\n\n return () => {\n clearTimeout(initialTimeout)\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current)\n }\n if (observerRef.current) {\n observerRef.current.disconnect()\n observerRef.current = null\n }\n }\n }, [isLocked, applyLockState, debouncedApplyLockState, findForm])\n\n // Listen for form submission to auto-lock after save\n useEffect(() => {\n const form = findForm()\n\n if (!form) {\n return\n }\n\n const handleSubmit = () => {\n // Lock after a delay to ensure save completes\n setTimeout(() => {\n setIsLocked(true)\n }, 2000)\n }\n\n form.addEventListener('submit', handleSubmit)\n\n return () => {\n form.removeEventListener('submit', handleSubmit)\n }\n }, [findForm])\n\n const toggleLock = useCallback(() => {\n setIsLocked((prev) => !prev)\n }, [isLocked])\n\n return (\n <div className=\"translation-settings-lock-container\">\n <button\n className={`translation-settings-lock-button ${isLocked ? 'locked' : 'unlocked'}`}\n onClick={toggleLock}\n title={\n isLocked\n ? '🔒 Settings are locked to prevent accidental changes. Click to unlock and edit.'\n : '🔓 Settings are unlocked. Click to lock after saving your changes.'\n }\n type=\"button\"\n >\n <span className=\"translation-settings-lock-icon\">{isLocked ? '🔒' : '🔓'}</span>\n <span className=\"translation-settings-lock-label\">\n {isLocked ? 'Unlock Settings' : 'Lock Settings'}\n </span>\n </button>\n </div>\n )\n}\n"],"names":["React","useCallback","useEffect","useRef","useState","TranslationSettingsLock","isLocked","setIsLocked","formRef","observerRef","debounceTimerRef","findForm","current","document","contains","formElement","querySelector","form","applyLockState","inputs","querySelectorAll","forEach","input","closest","setAttribute","classList","add","removeAttribute","remove","saveButtonElement","saveButton","debouncedApplyLockState","clearTimeout","setTimeout","initialTimeout","MutationObserver","observe","attributes","childList","subtree","disconnect","handleSubmit","addEventListener","removeEventListener","toggleLock","prev","div","className","button","onClick","title","type","span"],"mappings":"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"}
@@ -1,5 +1,3 @@
1
- /**
2
- * Client-side exports for the auto-translate plugin
3
- */
1
+ export { LockTranslation } from '../components/LockTranslation/index.js';
4
2
  export { TranslationControl } from '../components/TranslationControl.js';
5
3
  export type * from '../types/index.js';
@@ -1,5 +1,4 @@
1
- /**
2
- * Client-side exports for the auto-translate plugin
3
- */ export { TranslationControl } from '../components/TranslationControl.js';
1
+ export { LockTranslation } from '../components/LockTranslation/index.js';
2
+ export { TranslationControl } from '../components/TranslationControl.js';
4
3
 
5
4
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["/**\n * Client-side exports for the auto-translate plugin\n */\n\nexport { TranslationControl } from '../components/TranslationControl.js'\nexport type * from '../types/index.js'\n"],"names":["TranslationControl"],"mappings":"AAAA;;CAEC,GAED,SAASA,kBAAkB,QAAQ,sCAAqC"}
1
+ {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { LockTranslation } from '../components/LockTranslation/index.js'\nexport { TranslationControl } from '../components/TranslationControl.js'\nexport type * from '../types/index.js'\n"],"names":["LockTranslation","TranslationControl"],"mappings":"AAAA,SAASA,eAAe,QAAQ,yCAAwC;AACxE,SAASC,kBAAkB,QAAQ,sCAAqC"}