Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
782bbe3
initial commit
akshay-gupta7 Jul 1, 2026
85babc9
feat: gray out extended collections UI for non-enterprise Figma users
akshay-gupta7 Jul 1, 2026
c9fd483
feat: hide extended child themes in export list when export extended …
akshay-gupta7 Jul 1, 2026
898f9b4
fix: resolve test failures caused by extended collections branch changes
akshay-gupta7 Jul 1, 2026
814a1e1
remove test check in transaction
akshay-gupta7 Jul 1, 2026
cf72973
fix: gate extended collections logic behind hasExtendedCollections fl…
akshay-gupta7 Jul 1, 2026
4255903
fix: move isExtendedCollection param to end of setValuesOnVariable si…
akshay-gupta7 Jul 1, 2026
7ccc731
fix: only include collection in referenceVariableCandidate for extend…
akshay-gupta7 Jul 1, 2026
1af6088
fix: restore $figmaVariableReferences default and flatten $figmaIsExt…
akshay-gupta7 Jul 1, 2026
60d6bf3
fix: restore updateVariablesFromPlugin to sync path and gate collecti…
akshay-gupta7 Jul 1, 2026
5583a09
fix: default settings param to empty object in createNecessaryVariabl…
akshay-gupta7 Jul 1, 2026
1529a4c
fix: use object reference equality to guard self-references in update…
akshay-gupta7 Jul 1, 2026
b0b038f
fix: segregate extended collections logic to avoid breaking non-enter…
akshay-gupta7 Jul 1, 2026
120ab85
feat: auto-select parent theme when child is checked and lock parent …
akshay-gupta7 Jul 2, 2026
1572ecc
fix lint
akshay-gupta7 Jul 2, 2026
a80c3f8
address review comments
akshay-gupta7 Jul 2, 2026
18046a5
update initiator test
akshay-gupta7 Jul 2, 2026
b96922f
update test
akshay-gupta7 Jul 2, 2026
9a46aa2
fix: reliable inherit/override for extended collection variables
akshay-gupta7 Jul 6, 2026
8c143b8
remove debug logs
akshay-gupta7 Jul 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ const mockStartupParams: Omit<StartupMessage, 'licenseKey'> = {
usedEmail: null,
authData: null,
selectedExportThemes: [],
activeOrganizationId: null,
};

// #region helpers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,18 @@ export default function MultiFilesExport({ onClose }: Props) {
Object.entries(convertTokensToObject(tokens, storeTokenIdInJsonEditor)).forEach(([key, value]) => {
changeObj[`${key}.json`] = JSON.stringify(value, null, 2);
});
changeObj[`${SystemFilenames.THEMES}.json`] = JSON.stringify(themes, null, 2);
// Filter out internal metadata from themes before export
const cleanedThemes = themes.map((theme) => {
const {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
$figmaParentCollectionId,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
$figmaIsExtension,
...rest
} = theme;
return rest;
});
changeObj[`${SystemFilenames.THEMES}.json`] = JSON.stringify(cleanedThemes, null, 2);
const metadata = {
tokenSetOrder: Object.keys(tokens),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,18 @@ export default function SingleFileExport({ onClose }: Props) {
const exportData = React.useMemo(() => {
const returnValue = JSON.parse(formattedTokens);
if (includeAllTokens) {
set(returnValue, SystemFilenames.THEMES, themes);
// Filter out internal metadata from themes before export
const cleanedThemes = themes.map((theme) => {
const {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
$figmaParentCollectionId,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
$figmaIsExtension,
...rest
} = theme;
return rest;
});
set(returnValue, SystemFilenames.THEMES, cleanedThemes);
set(returnValue, SystemFilenames.METADATA, {
tokenSetOrder: Object.keys(tokens),
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useCallback, useEffect } from 'react';
import React, { useState, useCallback, useEffect, useMemo } from 'react';
import {
Button, Checkbox, Label, Stack, Heading,
} from '@tokens-studio/ui';
Expand All @@ -20,19 +20,35 @@ export default function ImportVariablesDialog({
const [useDimensions, setUseDimensions] = useState(false);
const [useRem, setUseRem] = useState(false);

// Initialize all collections as selected with all modes selected by default
// Filter out multi-level extensions (depth > 1)
const { allowedCollections, filteredCollections } = useMemo(() => {
const allowed: VariableCollectionInfo[] = [];
const filtered: VariableCollectionInfo[] = [];

collections.forEach((collection) => {
if (!collection.extensionDepth || collection.extensionDepth <= 1) {
allowed.push(collection);
} else {
filtered.push(collection);
}
});

return { allowedCollections: allowed, filteredCollections: filtered };
}, [collections]);

// Initialize all allowed collections as selected with all modes selected by default
useEffect(() => {
if (collections.length > 0) {
if (allowedCollections.length > 0) {
const initialSelection: SelectedCollections = {};
collections.forEach((collection) => {
allowedCollections.forEach((collection) => {
initialSelection[collection.id] = {
name: collection.name,
selectedModes: collection.modes.map((mode) => mode.modeId),
};
});
setSelectedCollections(initialSelection);
}
}, [collections]);
}, [allowedCollections]);

const handleCollectionToggle = useCallback((collectionId: string, collectionName: string, modes: { modeId: string; name: string }[]) => {
setSelectedCollections((prev) => {
Expand Down Expand Up @@ -104,19 +120,19 @@ export default function ImportVariablesDialog({
);

const hasSelections = Object.keys(selectedCollections).length > 0;
const allCollectionsSelected = collections.every((collection) => selectedCollections[collection.id]);
const allCollectionsSelected = allowedCollections.every((collection) => selectedCollections[collection.id]);

const handleToggleAllCollections = useCallback(() => {
// If all collections are selected, deselect all; otherwise, select all
if (allCollectionsSelected) {
setSelectedCollections({});
} else {
setSelectedCollections(collections.reduce((acc, collection) => ({
setSelectedCollections(allowedCollections.reduce((acc, collection) => ({
...acc,
[collection.id]: { name: collection.name, selectedModes: collection.modes.map((mode) => mode.modeId) },
}), {}));
}
}, [collections, allCollectionsSelected]);
}, [allowedCollections, allCollectionsSelected]);

return (
<Modal
Expand Down Expand Up @@ -183,16 +199,18 @@ export default function ImportVariablesDialog({
</Label>
</Stack>
</Stack>
{collections.length === 0 ? (
{allowedCollections.length === 0 ? (
<Box css={{
padding: '$3', backgroundColor: '$bgMuted', borderRadius: '$small', textAlign: 'center',
}}
>
There are no collections present in this file
{filteredCollections.length > 0
? 'All collections in this file have more than one level of extension'
: 'There are no collections present in this file'}
</Box>
) : (
<Stack direction="column" gap={3} css={{ borderLeft: '2px solid $borderMuted' }}>
{collections.map((collection) => {
{allowedCollections.map((collection) => {
const isCollectionSelected = !!selectedCollections[collection.id];
const selectedModes = selectedCollections[collection.id]?.selectedModes || [];

Expand Down Expand Up @@ -234,6 +252,15 @@ export default function ImportVariablesDialog({
)}
</Stack>

{filteredCollections.length > 0 && (
<Box css={{
padding: '$3', backgroundColor: '$warningBg', color: '$warningFg', borderRadius: '$small',
}}
>
⚠️ {filteredCollections.length} collection(s) skipped: We only support one level of extension at the moment.
</Box>
)}

{!hasSelections && (
<Box css={{
padding: '$3', backgroundColor: '$dangerBg', color: '$dangerFg', borderRadius: '$small',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ describe('Initiator', () => {
aliasBaseFontSize: '16',
applyVariablesStylesOrRawValue: ApplyVariablesStylesOrRawValues.VARIABLES_STYLES,
autoApplyThemeOnDrop: false,
exportExtendedCollections: false,
seenGenericVersionedHeaderMigrationDialog: false,
seenTermsUpdate2026: false,
});
Expand Down
Loading
Loading