diff --git a/.changeset/nine-waves-punch.md b/.changeset/nine-waves-punch.md new file mode 100644 index 0000000000..cfe4d59a4c --- /dev/null +++ b/.changeset/nine-waves-punch.md @@ -0,0 +1,5 @@ +--- +"@tokens-studio/figma-plugin": patch +--- + +Fix read-only access UI controls for Studio sync provider diff --git a/packages/tokens-studio-for-figma/src/app/components/Footer.tsx b/packages/tokens-studio-for-figma/src/app/components/Footer.tsx index 90bd4b9f90..9bbdad25d8 100644 --- a/packages/tokens-studio-for-figma/src/app/components/Footer.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/Footer.tsx @@ -103,9 +103,11 @@ export default function Footer() { size="small" tooltipSide="top" tooltip={ - t('pullFrom', { - provider: transformProviderName(storageType.provider), - }) as string + editProhibited + ? t('readOnly') + : (t('pullFrom', { + provider: transformProviderName(storageType.provider), + }) as string) } /> diff --git a/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/ManageThemesModal.tsx b/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/ManageThemesModal.tsx index c031c0f627..19cda286cd 100644 --- a/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/ManageThemesModal.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/ManageThemesModal.tsx @@ -7,7 +7,7 @@ import debounce from 'lodash.debounce'; import { Button, EmptyState } from '@tokens-studio/ui'; import { styled } from '@stitches/react'; import { useTranslation } from 'react-i18next'; -import { activeThemeSelector, themesListSelector } from '@/selectors'; +import { activeThemeSelector, editProhibitedSelector, themesListSelector } from '@/selectors'; import Modal from '../Modal'; import { Dispatch } from '@/app/store'; import Stack from '../Stack'; @@ -44,6 +44,7 @@ export const ManageThemesModal: React.FC(); const themes = useSelector(themesListSelector); const activeTheme = useSelector(activeThemeSelector); + const editProhibited = useSelector(editProhibitedSelector); const { confirm } = useConfirm(); const [themeEditorOpen, setThemeEditorOpen] = useState(false); const [themeListScrollPosition, setThemeListScrollPosition] = useState(0); @@ -202,6 +203,7 @@ export const ManageThemesModal: React.FC} onClick={handleToggleOpenThemeEditor} + disabled={editProhibited} > {t('newTheme')} @@ -215,6 +217,7 @@ export const ManageThemesModal: React.FC {t('delete')} @@ -233,6 +236,7 @@ export const ManageThemesModal: React.FC {t('saveTheme')} diff --git a/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/SingleThemeEntry.tsx b/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/SingleThemeEntry.tsx index bed4ae9586..8bc20cb5ea 100644 --- a/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/SingleThemeEntry.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/SingleThemeEntry.tsx @@ -8,7 +8,7 @@ import { ThemeObject } from '@/types'; import IconDiveInto from '@/icons/dive-into.svg'; import { TokenSetStatus } from '@/constants/TokenSetStatus'; import { Dispatch } from '@/app/store'; -import { activeThemeSelector } from '@/selectors'; +import { activeThemeSelector, editProhibitedSelector } from '@/selectors'; type Props = { theme: ThemeObject @@ -43,6 +43,7 @@ export const SingleThemeEntry: React.FC { const activeTheme = useSelector(activeThemeSelector); + const editProhibited = useSelector(editProhibitedSelector); const dispatch = useDispatch(); const tokenSetCount = useMemo(() => ( @@ -123,6 +124,7 @@ export const SingleThemeEntry: React.FC diff --git a/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/ThemeListGroupHeader.tsx b/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/ThemeListGroupHeader.tsx index 2b046bb0ab..ac25519320 100644 --- a/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/ThemeListGroupHeader.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/ManageThemesModal/ThemeListGroupHeader.tsx @@ -102,6 +102,7 @@ export function ThemeListGroupHeader({ size="small" variant="invisible" tooltip="Rename group" + disabled={editProhibited} /> ) : ( diff --git a/packages/tokens-studio-for-figma/src/app/components/TokenSetListItemContent.tsx b/packages/tokens-studio-for-figma/src/app/components/TokenSetListItemContent.tsx index 042d4f83e0..80697d3685 100644 --- a/packages/tokens-studio-for-figma/src/app/components/TokenSetListItemContent.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/TokenSetListItemContent.tsx @@ -76,9 +76,9 @@ export function TokenSetListItemContent({ item }: Parameters item={item} onCheck={handleCheckedChange} canEdit={!editProhibited} - canDuplicate={!tokenSetMetadata[item.path]?.isDynamic} + canDuplicate={!editProhibited && !tokenSetMetadata[item.path]?.isDynamic} canReorder={!editProhibited} - canDelete={!editProhibited || Object.keys(item.tokenSets).length > 1} + canDelete={!editProhibited && Object.keys(item.tokenSets).length > 1} onRename={item.onRename} onDelete={item.onDelete} onDuplicate={item.onDuplicate} diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/en/footer.json b/packages/tokens-studio-for-figma/src/i18n/lang/en/footer.json index 5df2299b76..2e6272d128 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/en/footer.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/en/footer.json @@ -4,5 +4,6 @@ "feedback": "Feedback", "pullFrom": "Pull from {{provider}}", "pushTo": "Push to {{provider}}", - "goTo": "Go to {{provider}}" + "goTo": "Go to {{provider}}", + "readOnly": "Read only" } diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/es/footer.json b/packages/tokens-studio-for-figma/src/i18n/lang/es/footer.json index 99ecb9d53b..0f69754722 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/es/footer.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/es/footer.json @@ -4,5 +4,6 @@ "feedback": "comentario", "pullFrom": "Extraer de {{provider}}", "pushTo": "Empuje a {{provider}}", - "goTo": "Ir a __MARCADOR__" + "goTo": "Ir a __MARCADOR__", + "readOnly": "Solo lectura" } diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/fr/footer.json b/packages/tokens-studio-for-figma/src/i18n/lang/fr/footer.json index b36bf934aa..0bce730a85 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/fr/footer.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/fr/footer.json @@ -4,5 +4,6 @@ "feedback": "retour", "pullFrom": "Tirer de {{provider}}", "pushTo": "Appuyez sur {{provider}}", - "goTo": "Aller à {{provider}}" + "goTo": "Aller à {{provider}}", + "readOnly": "Lecture seule" } diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/hi/footer.json b/packages/tokens-studio-for-figma/src/i18n/lang/hi/footer.json index 5c7b217a0e..e9d3d989fa 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/hi/footer.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/hi/footer.json @@ -4,5 +4,6 @@ "feedback": "प्रतिक्रिया", "pullFrom": "{{provider}} से खींचें", "pushTo": "{{provider}} पर पुश करें", - "goTo": "__प्लेसहोल्डर__ पर जाएँ" + "goTo": "__प्लेसहोल्डर__ पर जाएँ", + "readOnly": "केवल पढ़ने के लिए" } diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/nl/footer.json b/packages/tokens-studio-for-figma/src/i18n/lang/nl/footer.json index aee9d472de..9a37d303b9 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/nl/footer.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/nl/footer.json @@ -4,5 +4,6 @@ "feedback": "feedback", "pullFrom": "Trek uit {{provider}}", "pushTo": "Duwen naar {{provider}}", - "goTo": "Ga naar {{provider}}" + "goTo": "Ga naar {{provider}}", + "readOnly": "Alleen-lezen" } diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/zh/footer.json b/packages/tokens-studio-for-figma/src/i18n/lang/zh/footer.json index 0fb0ca0d0d..f18c11c80d 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/zh/footer.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/zh/footer.json @@ -4,5 +4,6 @@ "feedback": "反馈", "pullFrom": "从 {{provider}} 中提取", "pushTo": "推至 {{provider}}", - "goTo": "跳转到{{provider}}" + "goTo": "跳转到{{provider}}", + "readOnly": "只读" } diff --git a/packages/tokens-studio-for-figma/src/storage/TokensStudioTokenStorage.ts b/packages/tokens-studio-for-figma/src/storage/TokensStudioTokenStorage.ts index a82488e389..8bef693977 100644 --- a/packages/tokens-studio-for-figma/src/storage/TokensStudioTokenStorage.ts +++ b/packages/tokens-studio-for-figma/src/storage/TokensStudioTokenStorage.ts @@ -226,6 +226,21 @@ export class TokensStudioTokenStorage extends RemoteTokenStorage { + // For Tokens Studio, we don't have a direct API to check write permissions + // We return true optimistically and handle permission errors when they occur + // This will be used to set editProhibited state appropriately + try { + await this.ensureClient(); + // If we can initialize the client successfully, we assume write access + // If the user has read-only access, write operations will fail with permission errors + return true; + } catch (error) { + console.error('Failed to check write permissions:', error); + return false; + } + } + public async read(): Promise { let tokens: AnyTokenSet | null | undefined = {}; let themes: ThemeObjectsList = [];