├── .azuredevops └── pull_request_template.md ├── .gitattributes ├── .gitignore ├── Assets.meta ├── Assets ├── Panel Settings.asset ├── Panel Settings.asset.meta ├── UI Theme.tss ├── UI Theme.tss.meta ├── UnityBase.uss └── UnityBase.uss.meta ├── Editor.meta ├── Editor ├── Assets.meta ├── Assets │ ├── icon.png │ └── icon.png.meta ├── Core.meta ├── Core │ ├── Api.cs │ ├── Api.cs.meta │ ├── Assets.meta │ ├── Assets │ │ ├── AssetsInfo.cs │ │ ├── AssetsInfo.cs.meta │ │ ├── CachedAssets.cs │ │ ├── CachedAssets.cs.meta │ │ ├── GradientWriter.cs │ │ ├── GradientWriter.cs.meta │ │ ├── ImagesPostprocessor.cs │ │ └── ImagesPostprocessor.cs.meta │ ├── JsonUtility.cs │ ├── JsonUtility.cs.meta │ ├── NodeMetadata.cs │ ├── NodeMetadata.cs.meta │ ├── NodesRegistry.cs │ ├── NodesRegistry.cs.meta │ ├── RichText.meta │ ├── RichText │ │ ├── RichTextBuilder.cs │ │ └── RichTextBuilder.cs.meta │ ├── RootNodes.cs │ ├── RootNodes.cs.meta │ ├── StylesPreprocessor.cs │ ├── StylesPreprocessor.cs.meta │ ├── Uss.meta │ ├── Uss │ │ ├── BaseUssStyle.cs │ │ ├── BaseUssStyle.cs.meta │ │ ├── Properties.meta │ │ ├── Properties │ │ │ ├── AssetProperty.cs │ │ │ ├── AssetProperty.cs.meta │ │ │ ├── ColorProperty.cs │ │ │ ├── ColorProperty.cs.meta │ │ │ ├── CursorProperty.cs │ │ │ ├── CursorProperty.cs.meta │ │ │ ├── DurationProperty.cs │ │ │ ├── DurationProperty.cs.meta │ │ │ ├── EnumProperty.cs │ │ │ ├── EnumProperty.cs.meta │ │ │ ├── FlexProperty.cs │ │ │ ├── FlexProperty.cs.meta │ │ │ ├── IntegerProperty.cs │ │ │ ├── IntegerProperty.cs.meta │ │ │ ├── LayoutDouble4.cs │ │ │ ├── LayoutDouble4.cs.meta │ │ │ ├── Length2Property.cs │ │ │ ├── Length2Property.cs.meta │ │ │ ├── Length4Property.cs │ │ │ ├── Length4Property.cs.meta │ │ │ ├── LengthProperty.cs │ │ │ ├── LengthProperty.cs.meta │ │ │ ├── NumberProperty.cs │ │ │ ├── NumberProperty.cs.meta │ │ │ ├── ShadowProperty.cs │ │ │ └── ShadowProperty.cs.meta │ │ ├── StyleSlot.cs │ │ ├── StyleSlot.cs.meta │ │ ├── UssStyle.cs │ │ ├── UssStyle.cs.meta │ │ ├── UssWriter.cs │ │ └── UssWriter.cs.meta │ ├── Uxml.meta │ └── Uxml │ │ ├── UxmlBuilder.cs │ │ ├── UxmlBuilder.cs.meta │ │ ├── UxmlWriter.cs │ │ └── UxmlWriter.cs.meta ├── Extensions.meta ├── Extensions │ ├── Extensions.cs │ ├── Extensions.cs.meta │ ├── NodeExtensions.cs │ ├── NodeExtensions.cs.meta │ ├── UssStyleExtensions.cs │ └── UssStyleExtensions.cs.meta ├── Figma.Editor.asmdef ├── Figma.Editor.asmdef.meta ├── FigmaDownloader.cs ├── FigmaDownloader.cs.meta ├── FigmaWriter.cs ├── FigmaWriter.cs.meta ├── Inspector.meta ├── Inspector │ ├── AuthTest.cs │ ├── AuthTest.cs.meta │ ├── FigmaInspector.cs │ ├── FigmaInspector.cs.meta │ ├── Styles.cs │ └── Styles.cs.meta ├── Interface.meta └── Interface │ ├── Const.cs │ ├── Const.cs.meta │ ├── Enums.cs │ ├── Enums.cs.meta │ ├── Figma.Enums.cs │ ├── Figma.Enums.cs.meta │ ├── Figma.Types.Interface.cs │ ├── Figma.Types.Interface.cs.meta │ ├── Figma.Types.Structs.cs │ ├── Figma.Types.Structs.cs.meta │ ├── Figma.Types.cs │ ├── Figma.Types.cs.meta │ ├── Interface.Records.cs │ └── Interface.Records.cs.meta ├── License.md ├── License.md.meta ├── Prefabs.meta ├── Prefabs ├── Figma.prefab └── Figma.prefab.meta ├── Readme.md ├── Readme.md.meta ├── Runtime.meta ├── Runtime ├── AssemblyInfo.cs ├── AssemblyInfo.cs.meta ├── Core.meta ├── Core │ ├── Element.cs │ ├── Element.cs.meta │ ├── QueryAttribute.cs │ ├── QueryAttribute.cs.meta │ ├── UxmlAttribute.cs │ ├── UxmlAttribute.cs.meta │ ├── VisualElementMetadata.cs │ └── VisualElementMetadata.cs.meta ├── Extensions.meta ├── Extensions │ ├── EnumerableExtensions.cs │ ├── EnumerableExtensions.cs.meta │ ├── Extensions.cs │ ├── Extensions.cs.meta │ ├── PathExtensions.cs │ ├── PathExtensions.cs.meta │ ├── VisualElementExtensions.cs │ └── VisualElementExtensions.cs.meta ├── Figma.asmdef ├── Figma.asmdef.meta ├── Figma.cs ├── Figma.cs.meta ├── Interface.meta └── Interface │ ├── Core.meta │ ├── Core │ ├── SubElements.cs │ ├── SubElements.cs.meta │ ├── SyncElements.cs │ └── SyncElements.cs.meta │ ├── Enums.cs │ ├── Enums.cs.meta │ ├── Interface.Core.cs │ ├── Interface.Core.cs.meta │ ├── Interfaces.cs │ └── Interfaces.cs.meta ├── package.json ├── package.json.meta ├── ~Samples.meta └── ~Samples ├── Scripts.meta ├── Scripts ├── Figma.Samples.asmdef ├── Figma.Samples.asmdef.meta ├── Test.cs └── Test.cs.meta ├── Test.unity └── Test.unity.meta /.azuredevops/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ Before you merge ⚠️ 2 | 1. 👘 Ensure that your code follows the [codestyle](https://dev.azure.com/trackman/Golf/_wiki/wikis/Golf.wiki/35/Code-Style) 3 | 2. 🧠 Ensure that you created PRs for ALL branches 4 | 3. 🍄 Test your code by finishing one golf round (download fresh course if tools were changed) 5 | 4. 🖼️ Submit a screenshot of a finished golf round 6 | 5. 💾 Write a [proper](https://keepachangelog.com/en/1.0.0/) description -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Default 2 | * text=auto 3 | 4 | # Unity 5 | *.cs text 6 | *.shader text 7 | *.hlsl text 8 | *.cginc text 9 | 10 | # Unity YAML 11 | *.anim merge=unityyamlmerge auto 12 | *.asset merge=unityyamlmerge auto 13 | *.controller merge=unityyamlmerge auto 14 | *.mat merge=unityyamlmerge auto 15 | *.meta merge=unityyamlmerge auto 16 | *.physicsMaterial merge=unityyamlmerge auto 17 | *.physicsMaterial2D merge=unityyamlmerge auto 18 | *.prefab merge=unityyamlmerge auto 19 | *.unity merge=unityyamlmerge auto 20 | 21 | # Unity LFS 22 | *.cubemap filter=lfs diff=lfs merge=lfs 23 | *.unitypackage filter=lfs diff=lfs merge=lfs 24 | 25 | # Image 26 | *.ai filter=lfs diff=lfs merge=lfs 27 | *.apng filter=lfs diff=lfs merge=lfs 28 | *.astc filter=lfs diff=lfs merge=lfs 29 | *.bmp filter=lfs diff=lfs merge=lfs 30 | *.dds filter=lfs diff=lfs merge=lfs 31 | *.eps filter=lfs diff=lfs merge=lfs 32 | *.exr filter=lfs diff=lfs merge=lfs 33 | *.gif filter=lfs diff=lfs merge=lfs 34 | *.hdr filter=lfs diff=lfs merge=lfs 35 | *.jpeg filter=lfs diff=lfs merge=lfs 36 | *.jpg filter=lfs diff=lfs merge=lfs 37 | *.JPG filter=lfs diff=lfs merge=lfs 38 | *.ktx filter=lfs diff=lfs merge=lfs 39 | *.png filter=lfs diff=lfs merge=lfs 40 | *.PNG filter=lfs diff=lfs merge=lfs 41 | *.psd filter=lfs diff=lfs merge=lfs 42 | *.pvr filter=lfs diff=lfs merge=lfs 43 | *.svg filter=lfs diff=lfs merge=lfs 44 | *.svgz filter=lfs diff=lfs merge=lfs 45 | *.tga filter=lfs diff=lfs merge=lfs 46 | *.TGA filter=lfs diff=lfs merge=lfs 47 | *.tif filter=lfs diff=lfs merge=lfs 48 | *.tiff filter=lfs diff=lfs merge=lfs 49 | *.webm filter=lfs diff=lfs merge=lfs 50 | *.webp filter=lfs diff=lfs merge=lfs 51 | 52 | # Audio 53 | *.aif filter=lfs diff=lfs merge=lfs 54 | *.m4a filter=lfs diff=lfs merge=lfs 55 | *.mp3 filter=lfs diff=lfs merge=lfs 56 | *.ogg filter=lfs diff=lfs merge=lfs 57 | *.wav filter=lfs diff=lfs merge=lfs 58 | *.WAV filter=lfs diff=lfs merge=lfs 59 | 60 | # Video 61 | *.asf filter=lfs diff=lfs merge=lfs 62 | *.avi filter=lfs diff=lfs merge=lfs 63 | *.flv filter=lfs diff=lfs merge=lfs 64 | *.mov filter=lfs diff=lfs merge=lfs 65 | *.mp4 filter=lfs diff=lfs merge=lfs 66 | *.mpeg filter=lfs diff=lfs merge=lfs 67 | *.mpg filter=lfs diff=lfs merge=lfs 68 | *.ogv filter=lfs diff=lfs merge=lfs 69 | *.wmv filter=lfs diff=lfs merge=lfs 70 | 71 | # 3D Object 72 | *.blend filter=lfs diff=lfs merge=lfs 73 | *.dxf filter=lfs diff=lfs merge=lfs 74 | *.fbx filter=lfs diff=lfs merge=lfs 75 | *.FBX filter=lfs diff=lfs merge=lfs 76 | *.lxo filter=lfs diff=lfs merge=lfs 77 | *.ma filter=lfs diff=lfs merge=lfs 78 | *.max filter=lfs diff=lfs merge=lfs 79 | *.mb filter=lfs diff=lfs merge=lfs 80 | *.obj filter=lfs diff=lfs merge=lfs 81 | *.raw filter=lfs diff=lfs merge=lfs 82 | *.spm filter=lfs diff=lfs merge=lfs 83 | *.sbk filter=lfs diff=lfs merge=lfs 84 | 85 | # Compiled Dynamic Library 86 | *.dll filter=lfs diff=lfs merge=lfs 87 | *.pdb filter=lfs diff=lfs merge=lfs 88 | *.so filter=lfs diff=lfs merge=lfs 89 | *.bundle filter=lfs diff=lfs merge=lfs 90 | 91 | # Compiled Static Library 92 | *.a filter=lfs diff=lfs merge=lfs 93 | *.la filter=lfs diff=lfs merge=lfs 94 | *.lai filter=lfs diff=lfs merge=lfs 95 | *.lib filter=lfs diff=lfs merge=lfs 96 | *.llblgenproj filter=lfs diff=lfs merge=lfs 97 | 98 | # Font 99 | *.otf filter=lfs diff=lfs merge=lfs 100 | *.OTF filter=lfs diff=lfs merge=lfs 101 | *.ttf filter=lfs diff=lfs merge=lfs 102 | *.TTF filter=lfs diff=lfs merge=lfs 103 | *.pdf filter=lfs diff=lfs merge=lfs 104 | 105 | # CommandLine Utility 106 | *.exe filter=lfs diff=lfs merge=lfs 107 | 108 | # Archives 109 | *.zip filter=lfs diff=lfs merge=lfs 110 | *.rar filter=lfs diff=lfs merge=lfs 111 | *.7z filter=lfs diff=lfs merge=lfs -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Ll]ogs/ 4 | /[Oo]bj/ 5 | /[Bb]uild/ 6 | /[Bb]uilds/ 7 | /[Uu]ser[Ss]ettings/ 8 | /Assets/StreamingAssets* 9 | /Assets/UnityEngine* 10 | /Assets/Temp* 11 | /Packages/com.* 12 | /ProjectSettings/Packages* 13 | /ProjectSettings/boot.config 14 | /ProjectSettings/SceneTemplateSettings.json 15 | 16 | # Visual Studio cache directory 17 | /.vs/ 18 | /.vscode/ 19 | .vsconfig 20 | 21 | # Rider cache directory 22 | /.idea/ 23 | 24 | # Autogenerated VS/MD/Consulo solution and project files 25 | ExportedObj/ 26 | .consulo/ 27 | *.csproj 28 | *.unityproj 29 | *.sln 30 | *.suo 31 | *.tmp 32 | *.user 33 | *.userprefs 34 | *.pidb 35 | *.booproj 36 | *.svd 37 | *.pdb 38 | *.code* 39 | 40 | # Unity3D generated meta files 41 | *.pidb.meta 42 | 43 | # Unity3D generated packages 44 | packages-lock.json 45 | 46 | # Unity3D generated file on crash reports 47 | sysinfo.txt 48 | 49 | # Builds 50 | *.apk 51 | *.aab 52 | *.unitypackage 53 | 54 | # OSX files 55 | .DS_Store 56 | 57 | # Generated by analyzers 58 | /Assets/Default.ruleset 59 | /Assets/Default.ruleset.meta 60 | 61 | # Pipeline files 62 | *.netrc -------------------------------------------------------------------------------- /Assets.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7eedcbb7c4cee534cb8f55eaaa55ee65 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Panel Settings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &11400000 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 19101, guid: 0000000000000000e000000000000000, type: 0} 13 | m_Name: Panel Settings 14 | m_EditorClassIdentifier: 15 | themeUss: {fileID: -4733365628477956816, guid: 999af05a3e7a25744b50c66afc0ac938, type: 3} 16 | m_TargetTexture: {fileID: 0} 17 | m_ScaleMode: 2 18 | m_ReferenceSpritePixelsPerUnit: 100 19 | m_Scale: 1 20 | m_ReferenceDpi: 96 21 | m_FallbackDpi: 96 22 | m_ReferenceResolution: {x: 1920, y: 1080} 23 | m_ScreenMatchMode: 0 24 | m_Match: 0 25 | m_SortingOrder: 0 26 | m_TargetDisplay: 0 27 | m_ClearDepthStencil: 1 28 | m_ClearColor: 0 29 | m_ColorClearValue: {r: 0, g: 0, b: 0, a: 0} 30 | m_DynamicAtlasSettings: 31 | m_MinAtlasSize: 64 32 | m_MaxAtlasSize: 4096 33 | m_MaxSubTextureSize: 64 34 | m_ActiveFilters: 31 35 | m_AtlasBlitShader: {fileID: 9101, guid: 0000000000000000f000000000000000, type: 0} 36 | m_RuntimeShader: {fileID: 9100, guid: 0000000000000000f000000000000000, type: 0} 37 | m_RuntimeWorldShader: {fileID: 9102, guid: 0000000000000000f000000000000000, type: 0} 38 | textSettings: {fileID: 0} 39 | -------------------------------------------------------------------------------- /Assets/Panel Settings.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f486055d8ec653edc96c3f3c38380c8f 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 11400000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/UI Theme.tss: -------------------------------------------------------------------------------- 1 | @import url("unity-theme://default"); 2 | 3 | @import url("/Packages/com.trackman.figma/Assets/UnityBase.uss"); 4 | 5 | VisualElement {} 6 | -------------------------------------------------------------------------------- /Assets/UI Theme.tss.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 999af05a3e7a25744b50c66afc0ac938 3 | ScriptedImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 2 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | script: {fileID: 12388, guid: 0000000000000000e000000000000000, type: 0} 11 | disableValidation: 0 12 | -------------------------------------------------------------------------------- /Assets/UnityBase.uss: -------------------------------------------------------------------------------- 1 | :root { 2 | --selection-color: #5890DE; 3 | --cursor-color: #FFFFFF; 4 | } 5 | 6 | .unity-base-field { 7 | flex-direction: row; 8 | margin: initial; 9 | overflow: hidden; 10 | flex-shrink: 0; 11 | --unity-sync-text-editor-engine: true; 12 | } 13 | 14 | .unity-base-text-field { 15 | white-space: nowrap; 16 | --unity-selection-color: var(--selection-color); 17 | --unity-cursor-color: var(--cursor-color); 18 | } 19 | 20 | .unity-base-field__input { 21 | flex: 1 0 0; 22 | overflow: hidden; 23 | margin: initial; 24 | } 25 | 26 | .unity-base-text-field__input { 27 | padding: initial; 28 | border-radius: initial; 29 | cursor: initial; 30 | -unity-overflow-clip-box: content-box; 31 | flex: 1 1 auto; 32 | background-color: initial; 33 | border-color: initial; 34 | border-width: initial; 35 | margin: initial; 36 | --unity-sync-text-editor-engine: true; 37 | } 38 | 39 | .unity-base-text-field__input:focus, 40 | .unity-base-text-field__input:hover, 41 | .unity-base-text-field:focus > .unity-base-text-field__input, 42 | .unity-base-text-field:hover > .unity-base-text-field__input { 43 | border-color: var(--selection-color); 44 | } 45 | 46 | .unity-base-slider, .unity-base-slider__dragger, .unity-base-slider__tracker { 47 | border-width: initial; 48 | border-color: initial; 49 | background-color: initial; 50 | } 51 | .unity-base-slider:focus, .unity-base-slider__dragger, .unity-base-slider__tracker { 52 | border-color: initial; 53 | } 54 | 55 | .unity-base-slider--vertical, .unity-base-slider__dragger, .unity-base-slider__tracker { 56 | margin: initial; 57 | } 58 | 59 | .unity-scroller, .unity-base-slider__dragger, .unity-base-slider__tracker { 60 | border-color: initial; 61 | background-color: initial; 62 | } 63 | 64 | .unity-scroller--vertical, .unity-base-slider__dragger, .unity-base-slider__tracker { 65 | left: initial; 66 | top: initial; 67 | margin: initial; 68 | border-width: initial; 69 | border-color: initial; 70 | background-color: initial; 71 | } 72 | .unity-scroller--vertical > .unity-scroller__slider { 73 | margin: initial; 74 | width: initial; 75 | } -------------------------------------------------------------------------------- /Assets/UnityBase.uss.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d97716672acb0ee45bf6697e5adadcdd 3 | ScriptedImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 2 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} 11 | disableValidation: 0 12 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 047c80ffe3f35194089fa29faafd00ff 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Assets.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5b1014bd7c33f18afb1f58ad7f928370 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Assets/icon.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a455e4bc91a73baa0a7c6ccbfe0e3d9a965ebcc488b4e773ab540f54a6784b1c 3 | size 1218 4 | -------------------------------------------------------------------------------- /Editor/Assets/icon.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c0cc7a4d45c57758c894e807bb1af36b 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 13 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 0 41 | wrapV: 0 42 | wrapW: 0 43 | nPOTScale: 1 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 0 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 100 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 0 56 | spriteTessellationDetail: -1 57 | textureType: 0 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 64 73 | resizeAlgorithm: 1 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | spriteSheet: 97 | serializedVersion: 2 98 | sprites: [] 99 | outline: [] 100 | physicsShape: [] 101 | bones: [] 102 | spriteID: 103 | internalID: 0 104 | vertices: [] 105 | indices: 106 | edges: [] 107 | weights: [] 108 | secondaryTextures: [] 109 | nameFileIdTable: {} 110 | mipmapLimitGroupName: 111 | pSDRemoveMatte: 0 112 | userData: 113 | assetBundleName: 114 | assetBundleVariant: 115 | -------------------------------------------------------------------------------- /Editor/Core.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7101b854596dd674dad786dffc6936ab 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Core/Api.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Figma 7 | { 8 | using Internals; 9 | 10 | internal abstract class Api : IDisposable 11 | { 12 | #region Fields 13 | protected readonly string fileKey; 14 | protected readonly HttpClient httpClient; 15 | #endregion 16 | 17 | #region Constructors 18 | protected Api(string personalAccessToken, string fileKey) 19 | { 20 | this.fileKey = fileKey; 21 | httpClient = new(); 22 | httpClient.DefaultRequestHeaders.Add("X-FIGMA-TOKEN", personalAccessToken); 23 | } 24 | #endregion 25 | 26 | #region Methods 27 | void IDisposable.Dispose() => httpClient.Dispose(); 28 | #endregion 29 | 30 | #region Support Methods 31 | protected async Task ConvertOnBackgroundAsync(string json, CancellationToken token) where T : class => await Task.Run(() => Task.FromResult(JsonUtility.FromJson(json)), token); 32 | protected async Task GetAsync(string get, CancellationToken token = default) where T : class => await ConvertOnBackgroundAsync(await GetJsonAsync(get, token), token); 33 | protected async Task GetJsonAsync(string get, CancellationToken token = default) => await HttpGetAsync($"{Const.api}/{get}", token); 34 | async Task HttpGetAsync(string url, CancellationToken token = default) 35 | { 36 | using HttpRequestMessage request = new(HttpMethod.Get, url); 37 | HttpResponseMessage response = await httpClient.SendAsync(request, token); 38 | 39 | if (response.IsSuccessStatusCode) 40 | return await response.Content.ReadAsStringAsync(); 41 | 42 | throw new HttpRequestException($"{HttpMethod.Get} {url} {response.StatusCode.ToString()}"); 43 | } 44 | #endregion 45 | } 46 | } -------------------------------------------------------------------------------- /Editor/Core/Api.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3206e4d9910843d38b90ab361b9a297b 3 | timeCreated: 1696830500 -------------------------------------------------------------------------------- /Editor/Core/Assets.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 569bf8b4580a4bd792f747a7a503034c 3 | timeCreated: 1733918411 -------------------------------------------------------------------------------- /Editor/Core/Assets/AssetsInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Reflection; 6 | using Unity.VectorGraphics.Editor; 7 | using UnityEditor; 8 | using UnityEngine; 9 | using Object = UnityEngine.Object; 10 | 11 | namespace Figma.Core.Assets 12 | { 13 | using Internals; 14 | using static Internals.Const; 15 | 16 | internal class AssetsInfo 17 | { 18 | #region Fields 19 | internal readonly string directory; 20 | internal readonly string relativeDirectory; 21 | internal readonly CachedAssets cachedAssets; 22 | internal readonly ConcurrentBag modifiedContent; 23 | 24 | readonly IReadOnlyList fontDirectories; 25 | #endregion 26 | 27 | #region Constructors 28 | internal AssetsInfo(string directory, string relativeDirectory, string remapsFileName, IReadOnlyList fontDirectories) 29 | { 30 | this.directory = directory; 31 | this.relativeDirectory = relativeDirectory; 32 | this.fontDirectories = fontDirectories; 33 | 34 | modifiedContent = new ConcurrentBag(); 35 | cachedAssets = new CachedAssets(directory, remapsFileName); 36 | } 37 | #endregion 38 | 39 | #region Methods 40 | internal (bool exists, string path) GetAssetPath(string name, string extension) 41 | { 42 | switch (extension) 43 | { 44 | case KnownFormats.otf or KnownFormats.ttf: 45 | string fontPath = GetFontPath(name, extension); 46 | return (fontPath.NotNullOrEmpty(), fontPath); 47 | 48 | case KnownFormats.asset: 49 | string fontAssetPath = GetFontPath(name, extension); 50 | string fontDirectoryPath = Path.GetDirectoryName(fontAssetPath); 51 | string target = string.IsNullOrEmpty(fontDirectoryPath) ? $"{name} SDF.{extension}" : PathExtensions.CombinePath(fontDirectoryPath, $"{name} SDF.{extension}"); 52 | return (fontAssetPath.NotNullOrEmpty(), target); 53 | 54 | case KnownFormats.png or KnownFormats.svg: 55 | string mappedName = cachedAssets[name]; 56 | string filename = PathExtensions.CombinePath(imagesDirectoryName, $"{mappedName}.{extension}"); 57 | return (File.Exists(PathExtensions.CombinePath(directory, filename)), filename); 58 | 59 | default: 60 | throw new NotSupportedException(extension); 61 | } 62 | } 63 | internal (bool valid, int width, int height) GetAssetSize(string name, string extension) 64 | { 65 | (bool valid, string path) = GetAssetPath(name, extension); 66 | switch (extension) 67 | { 68 | case KnownFormats.png: 69 | if (!valid) 70 | return (false, -1, -1); 71 | 72 | TextureImporter importer = (TextureImporter)AssetImporter.GetAtPath(PathExtensions.CombinePath(relativeDirectory, path)); 73 | importer.GetSourceTextureWidthAndHeight(out int width, out int height); 74 | return (true, width, height); 75 | 76 | case KnownFormats.svg: 77 | if (!valid) 78 | return (false, -1, -1); 79 | 80 | SVGImporter svgImporter = (SVGImporter)AssetImporter.GetAtPath(PathExtensions.CombinePath(relativeDirectory, path)); 81 | Object vectorImage = AssetDatabase.LoadMainAssetAtPath(PathExtensions.CombinePath(relativeDirectory, path)); 82 | 83 | if (!svgImporter || !vectorImage) 84 | return (false, -1, -1); 85 | 86 | if (vectorImage.GetType().GetField("size", BindingFlags.NonPublic | BindingFlags.Instance) is not { } fieldInfo) 87 | return (true, svgImporter ? svgImporter.TextureWidth : -1, svgImporter ? svgImporter.TextureHeight : -1); 88 | 89 | Vector2 size = (Vector2)fieldInfo.GetValue(vectorImage); 90 | return (true, Mathf.CeilToInt(size.x), Mathf.CeilToInt(size.y)); 91 | 92 | default: 93 | throw new NotSupportedException(extension); 94 | } 95 | } 96 | internal void AddModifiedFiles(params string[] items) => items.ForEach(item => modifiedContent.Add(item)); 97 | internal string GetAbsolutePath(string path) => PathExtensions.CombinePath(directory, path); 98 | #endregion 99 | 100 | #region Support Methods 101 | string GetFontPath(string name, string extension) 102 | { 103 | string localFontsPath = PathExtensions.CombinePath(fontsDirectoryName, $"{name}.{extension}"); 104 | 105 | if (File.Exists(FileUtil.GetPhysicalPath(PathExtensions.CombinePath(relativeDirectory, localFontsPath)))) 106 | return "/" + PathExtensions.CombinePath(relativeDirectory, localFontsPath); 107 | 108 | foreach (string fontsDirectory in fontDirectories) 109 | { 110 | string projectFontPath = PathExtensions.CombinePath(fontsDirectory, $"{name}.{extension}"); 111 | if (File.Exists(FileUtil.GetPhysicalPath(projectFontPath))) 112 | return "/" + projectFontPath; 113 | } 114 | 115 | return null; 116 | } 117 | #endregion 118 | } 119 | } -------------------------------------------------------------------------------- /Editor/Core/Assets/AssetsInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 45f0ac71191d448fb945cd8b96c71dd0 3 | timeCreated: 1733916355 -------------------------------------------------------------------------------- /Editor/Core/Assets/CachedAssets.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Figma.Core.Assets 7 | { 8 | using Internals; 9 | using static Internals.PathExtensions; 10 | 11 | internal sealed class CachedAssets 12 | { 13 | #region Fields 14 | readonly string targetFilePath; 15 | #endregion 16 | 17 | #region Constructor 18 | internal CachedAssets(string directory, string name) => targetFilePath = CombinePath(directory, $"{nameof(CachedAssets)}-{name}.{KnownFormats.json}"); 19 | #endregion 20 | 21 | #region Properties 22 | internal Dictionary Map { get; private set; } 23 | #endregion 24 | 25 | #region Operators 26 | internal string this[string key] 27 | { 28 | get => Map.GetValueOrDefault(key, key); 29 | set => Map[key] = value; 30 | } 31 | #endregion 32 | 33 | #region Methods 34 | internal async Task LoadAsync(CancellationToken token) => Map = File.Exists(targetFilePath) ? JsonUtility.FromJson>(await File.ReadAllTextAsync(targetFilePath, token)) : new(); 35 | internal async Task SaveAsync() => await File.WriteAllTextAsync(targetFilePath, JsonUtility.ToJson(Map, prettyPrint: true)); 36 | #endregion 37 | } 38 | } -------------------------------------------------------------------------------- /Editor/Core/Assets/CachedAssets.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 17a02e0c1cba42429bc345ea5794a854 3 | timeCreated: 1733918400 -------------------------------------------------------------------------------- /Editor/Core/Assets/GradientWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using System.Xml; 5 | using UnityEngine; 6 | 7 | namespace Figma.Core.Assets 8 | { 9 | using Internals; 10 | using static Internals.Const; 11 | 12 | internal class GradientWriter : IDisposable 13 | { 14 | #region Fields 15 | static readonly XmlWriterSettings xmlWriterSettings = new() 16 | { 17 | Indent = true, 18 | NewLineOnAttributes = true, 19 | NewLineChars = Environment.NewLine, 20 | IndentChars = indentCharacters, 21 | Async = true 22 | }; 23 | 24 | readonly XmlWriter writer; 25 | #endregion 26 | 27 | #region Constructors 28 | public GradientWriter(string xmlPath) => writer = XmlWriter.Create(xmlPath, xmlWriterSettings); 29 | #endregion 30 | 31 | #region Methods 32 | public async Task WriteAsync(GradientPaint gradient, CancellationToken token) 33 | { 34 | writer.WriteStartElement(KnownFormats.svg); 35 | writer.WriteStartElement("defs"); 36 | switch (gradient.type) 37 | { 38 | case PaintType.GRADIENT_LINEAR: 39 | writer.WriteStartElement("linearGradient"); 40 | writer.WriteAttributeString("id", nameof(gradient)); 41 | for (int i = 0; i < Mathf.Max(gradient.gradientHandlePositions.Length, 2); ++i) 42 | { 43 | writer.WriteAttributeString($"x{i + 1}", gradient.gradientHandlePositions[i].x.ToString("F2", Culture)); 44 | writer.WriteAttributeString($"y{i + 1}", gradient.gradientHandlePositions[i].y.ToString("F2", Culture)); 45 | } 46 | 47 | break; 48 | 49 | case PaintType.GRADIENT_RADIAL: 50 | case PaintType.GRADIENT_DIAMOND: 51 | writer.WriteStartElement("radialGradient"); 52 | writer.WriteAttributeString("id", nameof(gradient)); 53 | writer.WriteAttributeString("fx", gradient.gradientHandlePositions[0].x.ToString("F2", Culture)); 54 | writer.WriteAttributeString("fy", gradient.gradientHandlePositions[0].y.ToString("F2", Culture)); 55 | writer.WriteAttributeString("cx", gradient.gradientHandlePositions[0].x.ToString("F2", Culture)); 56 | writer.WriteAttributeString("cy", gradient.gradientHandlePositions[0].y.ToString("F2", Culture)); 57 | 58 | Vector2 a = new ((float)gradient.gradientHandlePositions[1].x, (float)gradient.gradientHandlePositions[1].y); 59 | Vector2 b = new ((float)gradient.gradientHandlePositions[0].x, (float)gradient.gradientHandlePositions[0].y); 60 | float radius = (a - b).magnitude; 61 | writer.WriteAttributeString("r", radius.ToString("F2", Culture)); 62 | break; 63 | 64 | default: 65 | throw new NotSupportedException(); 66 | } 67 | 68 | foreach (ColorStop stop in gradient.gradientStops) 69 | { 70 | writer.WriteStartElement(nameof(stop)); 71 | writer.WriteAttributeString("offset", stop.position.ToString("F2", Culture)); 72 | writer.WriteAttributeString("style", $"stop-color:rgb({(byte)(stop.color.r * 255)},{(byte)(stop.color.g * 255)},{(byte)(stop.color.b * 255)});stop-opacity:{stop.color.a.ToString("F2", Culture)}"); 73 | await writer.WriteEndElementAsync(); 74 | } 75 | 76 | await writer.WriteEndElementAsync(); 77 | token.ThrowIfCancellationRequested(); 78 | 79 | await writer.WriteEndElementAsync(); 80 | token.ThrowIfCancellationRequested(); 81 | 82 | writer.WriteStartElement("rect"); 83 | writer.WriteAttributeString("width", "100"); 84 | writer.WriteAttributeString("height", "100"); 85 | writer.WriteAttributeString("fill", "url(#gradient)"); 86 | 87 | if (gradient.opacity < 1.0) 88 | writer.WriteAttributeString("fill-opacity", gradient.opacity.ToString("F2", Culture)); 89 | 90 | await writer.WriteEndElementAsync(); 91 | token.ThrowIfCancellationRequested(); 92 | 93 | await writer.WriteEndElementAsync(); 94 | token.ThrowIfCancellationRequested(); 95 | } 96 | public void Dispose() => writer?.Close(); 97 | #endregion 98 | } 99 | } -------------------------------------------------------------------------------- /Editor/Core/Assets/GradientWriter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ec2d941c5d164f0b8887c69795bd3767 3 | timeCreated: 1734429409 -------------------------------------------------------------------------------- /Editor/Core/Assets/ImagesPostprocessor.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using Unity.VectorGraphics.Editor; 4 | using UnityEditor; 5 | 6 | #pragma warning disable S1144 // Called from Unity 7 | 8 | namespace Figma.Core.Assets 9 | { 10 | using Internals; 11 | using static Internals.Const; 12 | 13 | internal class ImagesPostprocessor : AssetPostprocessor 14 | { 15 | #region Methods 16 | void OnPreprocessAsset() 17 | { 18 | DirectoryInfo parentDirectory = Directory.GetParent(assetPath)?.Parent; 19 | 20 | if (parentDirectory is null || !Directory.GetFiles(parentDirectory!.FullName, "*." + KnownFormats.uxml).Any()) 21 | return; 22 | 23 | if (assetPath.Contains(imagesDirectoryName) && assetImporter is SVGImporter svgImporter) 24 | svgImporter.SvgType = SVGType.UIToolkit; 25 | 26 | if (!assetPath.Contains(imagesDirectoryName) || assetImporter is not TextureImporter textureImporter) 27 | return; 28 | 29 | textureImporter.npotScale = TextureImporterNPOTScale.None; 30 | textureImporter.mipmapEnabled = false; 31 | 32 | TextureImporterPlatformSettings androidOverrides = textureImporter.GetPlatformTextureSettings("Android"); 33 | androidOverrides.overridden = true; 34 | androidOverrides.format = TextureImporterFormat.ETC2_RGBA8Crunched; 35 | androidOverrides.compressionQuality = 90; 36 | textureImporter.SetPlatformTextureSettings(androidOverrides); 37 | } 38 | #endregion 39 | } 40 | } -------------------------------------------------------------------------------- /Editor/Core/Assets/ImagesPostprocessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 830782a47c09eebd4b66c78e8b91c6a7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Core/JsonUtility.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Buffers; 5 | using System.IO; 6 | using UnityEngine; 7 | 8 | namespace Figma.Internals 9 | { 10 | public class JsonUtility 11 | { 12 | class ArrayPool : IArrayPool 13 | { 14 | #region Methods 15 | public char[] Rent(int minimumLength) => ArrayPool.Shared.Rent(minimumLength); 16 | public void Return(char[] array) => ArrayPool.Shared.Return(array); 17 | #endregion 18 | } 19 | 20 | #region Properties 21 | static JsonSerializer serializer = new(); 22 | 23 | static readonly IArrayPool arrayPool = new ArrayPool(); 24 | #endregion 25 | 26 | #region Constructors 27 | #if UNITY_EDITOR 28 | [UnityEditor.InitializeOnLoadMethod] 29 | #endif 30 | [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] 31 | public static void Initialize() 32 | { 33 | JsonSerializerSettings settings = new() 34 | { 35 | NullValueHandling = NullValueHandling.Ignore, 36 | MissingMemberHandling = MissingMemberHandling.Ignore, 37 | Converters = 38 | { 39 | new EffectArrayConverter(), 40 | new PaintArrayConverter(), 41 | new LayoutGridArrayConverter(), 42 | new ExportSettingsArrayConverter(), 43 | new TransitionConverter(), 44 | new BaseNodeArrayConverter(), 45 | new SceneNodeArrayConverter() 46 | } 47 | }; 48 | serializer = JsonSerializer.Create(settings); 49 | } 50 | public static string ToJson(T value, bool prettyPrint) 51 | { 52 | using StringWriter stringWriter = new(); 53 | using JsonTextWriter jsonTextWriter = new(stringWriter) { Formatting = prettyPrint ? Formatting.Indented : Formatting.None }; 54 | serializer.Serialize(jsonTextWriter, value); 55 | return stringWriter.ToString(); 56 | } 57 | public static T FromJson(string json, bool useArrayPool = true) 58 | { 59 | using StringReader stringReader = new(json); 60 | using JsonTextReader jsonTextReader = new(stringReader); 61 | if (useArrayPool) 62 | jsonTextReader.ArrayPool = arrayPool; 63 | 64 | return serializer.Deserialize(jsonTextReader); 65 | } 66 | #endregion 67 | } 68 | 69 | public abstract class ArrayConverter : JsonConverter 70 | { 71 | #region Methods 72 | public override bool CanConvert(Type objectType) => objectType == typeof(T[]); 73 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 74 | { 75 | JArray array = JArray.Load(reader); 76 | T[] result = new T[array.Count]; 77 | 78 | for (int i = 0; i < array.Count; ++i) 79 | result[i] = ToObject((JObject)array[i], serializer); 80 | 81 | return result; 82 | } 83 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 84 | { 85 | T[] array = (T[])value; 86 | writer.WriteStartArray(); 87 | 88 | foreach (T node in array) 89 | serializer.Serialize(writer, node); 90 | 91 | writer.WriteEndArray(); 92 | } 93 | protected TEnum GetValue(JObject obj, string name = "type") => (TEnum)Enum.Parse(typeof(TEnum), obj[name].Value()); 94 | protected abstract T ToObject(JObject obj, JsonSerializer serializer); 95 | #endregion 96 | } 97 | 98 | public class EffectArrayConverter : ArrayConverter 99 | { 100 | #region Methods 101 | protected override Effect ToObject(JObject obj, JsonSerializer serializer) => 102 | GetValue(obj) switch 103 | { 104 | EffectType.INNER_SHADOW => obj.ToObject(serializer), 105 | EffectType.DROP_SHADOW => obj.ToObject(serializer), 106 | EffectType.LAYER_BLUR => obj.ToObject(serializer), 107 | EffectType.BACKGROUND_BLUR => obj.ToObject(serializer), 108 | _ => throw new NotSupportedException() 109 | }; 110 | #endregion 111 | } 112 | 113 | public class PaintArrayConverter : ArrayConverter 114 | { 115 | #region Methods 116 | protected override Paint ToObject(JObject obj, JsonSerializer serializer) => 117 | GetValue(obj) switch 118 | { 119 | PaintType.SOLID => obj.ToObject(serializer), 120 | PaintType.GRADIENT_LINEAR => obj.ToObject(serializer), 121 | PaintType.GRADIENT_RADIAL => obj.ToObject(serializer), 122 | PaintType.GRADIENT_ANGULAR => obj.ToObject(serializer), 123 | PaintType.GRADIENT_DIAMOND => obj.ToObject(serializer), 124 | PaintType.IMAGE => obj.ToObject(serializer), 125 | PaintType.EMOJI => obj.ToObject(serializer), 126 | _ => throw new NotSupportedException() 127 | }; 128 | #endregion 129 | } 130 | 131 | public class LayoutGridArrayConverter : ArrayConverter 132 | { 133 | #region Methods 134 | protected override LayoutGrid ToObject(JObject obj, JsonSerializer serializer) => 135 | GetValue(obj, "pattern") switch 136 | { 137 | Pattern.COLUMNS => obj.ToObject(serializer), 138 | Pattern.ROWS => obj.ToObject(serializer), 139 | Pattern.GRID => obj.ToObject(serializer), 140 | _ => throw new NotSupportedException() 141 | }; 142 | #endregion 143 | } 144 | 145 | public class ExportSettingsArrayConverter : ArrayConverter 146 | { 147 | #region Methods 148 | protected override ExportSettings ToObject(JObject obj, JsonSerializer serializer) => 149 | GetValue(obj, "format") switch 150 | { 151 | Format.JPG => obj.ToObject(serializer), 152 | Format.PNG => obj.ToObject(serializer), 153 | Format.SVG => obj.ToObject(serializer), 154 | Format.PDF => obj.ToObject(serializer), 155 | _ => throw new NotSupportedException() 156 | }; 157 | #endregion 158 | } 159 | 160 | public class TransitionConverter : JsonConverter 161 | { 162 | #region Methods 163 | public override bool CanConvert(Type objectType) => objectType == typeof(Transition); 164 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 165 | { 166 | JObject obj = JObject.Load(reader); 167 | return (TransitionType)Enum.Parse(typeof(TransitionType), obj["type"]!.Value()) switch 168 | { 169 | TransitionType.DISSOLVE => obj.ToObject(serializer), 170 | TransitionType.SMART_ANIMATE => obj.ToObject(serializer), 171 | TransitionType.MOVE_IN => obj.ToObject(serializer), 172 | TransitionType.MOVE_OUT => obj.ToObject(serializer), 173 | TransitionType.PUSH => obj.ToObject(serializer), 174 | TransitionType.SLIDE_IN => obj.ToObject(serializer), 175 | TransitionType.SLIDE_OUT => obj.ToObject(serializer), 176 | _ => throw new NotSupportedException() 177 | }; 178 | } 179 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException(); 180 | #endregion 181 | } 182 | 183 | public class BaseNodeArrayConverter : ArrayConverter 184 | { 185 | #region Methods 186 | protected override BaseNode ToObject(JObject obj, JsonSerializer serializer) => 187 | GetValue(obj) switch 188 | { 189 | NodeType.DOCUMENT => obj.ToObject(serializer), 190 | NodeType.CANVAS => obj.ToObject(serializer), 191 | _ => throw new NotSupportedException() 192 | }; 193 | #endregion 194 | } 195 | 196 | public class SceneNodeArrayConverter : ArrayConverter 197 | { 198 | #region Methods 199 | protected override SceneNode ToObject(JObject obj, JsonSerializer serializer) => 200 | GetValue(obj) switch 201 | { 202 | NodeType.SLICE => obj.ToObject(serializer), 203 | NodeType.FRAME => obj.ToObject(serializer), 204 | NodeType.GROUP => obj.ToObject(serializer), 205 | NodeType.COMPONENT_SET => obj.ToObject(serializer), 206 | NodeType.COMPONENT => obj.ToObject(serializer), 207 | NodeType.INSTANCE => obj.ToObject(serializer), 208 | NodeType.BOOLEAN_OPERATION => obj.ToObject(serializer), 209 | NodeType.VECTOR => obj.ToObject(serializer), 210 | NodeType.STAR => obj.ToObject(serializer), 211 | NodeType.LINE => obj.ToObject(serializer), 212 | NodeType.ELLIPSE => obj.ToObject(serializer), 213 | NodeType.REGULAR_POLYGON => obj.ToObject(serializer), 214 | NodeType.RECTANGLE => obj.ToObject(serializer), 215 | NodeType.TEXT => obj.ToObject(serializer), 216 | NodeType.SECTION => obj.ToObject(serializer), 217 | _ => throw new NotSupportedException() 218 | }; 219 | #endregion 220 | } 221 | 222 | public class FigmaGeneration { } 223 | } -------------------------------------------------------------------------------- /Editor/Core/JsonUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c03fe5a0d0b4de848ae40e0ab3bfaac6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Core/NodeMetadata.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 826eb9624b2845ffafc480902c5ed5d6 3 | timeCreated: 1696228041 -------------------------------------------------------------------------------- /Editor/Core/NodesRegistry.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Figma.Core 5 | { 6 | using Internals; 7 | 8 | internal sealed class NodesRegistry 9 | { 10 | #region Fields 11 | internal List MissingComponents { get; } = new(Const.initialCollectionCapacity); 12 | internal List ImageFills { get; } = new(Const.initialCollectionCapacity); 13 | internal List Pngs { get; } = new(Const.initialCollectionCapacity); 14 | internal List Svgs { get; } = new(Const.initialCollectionCapacity); 15 | internal Dictionary Gradients { get; } = new(Const.initialCollectionCapacity); 16 | #endregion 17 | 18 | public NodesRegistry(Data data, NodeMetadata nodeMetadata) 19 | { 20 | List nodes = data.document.children.SelectMany(canvas => canvas.Flatten(node => node.IsVisible() && 21 | nodeMetadata.EnabledInHierarchy(node) && 22 | node.parent is not BooleanOperationNode)).ToList(); 23 | 24 | MissingComponents.AddRange(nodes.OfType() 25 | .Where(instance => data.document.Flatten().Any(node => node.id == instance.componentId)) 26 | .Select(instance => instance.componentId)); 27 | 28 | Pngs.AddRange(nodes.Where(node => node is not BooleanOperationNode && node.IsSvgNode() && node.HasImage())); 29 | Svgs.AddRange(nodes.Where(node => node.IsSvgNode() && !node.HasImage())); 30 | ImageFills.AddRange(nodes.Where(node => node is not BooleanOperationNode && !node.IsSvgNode() && node.HasImage())); 31 | 32 | foreach (GradientPaint gradient in nodes.OfType() 33 | .Where(x => x is not BooleanOperationNode) 34 | .SelectMany(x => x.fills.OfType())) 35 | Gradients.TryAdd(gradient.GetHash(), gradient); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Editor/Core/NodesRegistry.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e6f8d15f8d194578ad29c8c0a4a0d2e6 3 | timeCreated: 1734424985 -------------------------------------------------------------------------------- /Editor/Core/RichText.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b4773ce3ee3d4f9f817a7dca740b3afc 3 | timeCreated: 1743683652 -------------------------------------------------------------------------------- /Editor/Core/RichText/RichTextBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | namespace Figma.Core.RichText 8 | { 9 | using Internals; 10 | 11 | internal sealed class TextBuilder 12 | { 13 | #region Container 14 | enum TagType 15 | { 16 | Bold, 17 | Italic, 18 | Underline, 19 | Strikethrough, 20 | Color, 21 | FontSize, 22 | FontWeight, 23 | Indent, 24 | } 25 | 26 | class Tag 27 | { 28 | #region Fields 29 | readonly string tag; 30 | readonly StringBuilder stringBuilder; 31 | 32 | string value; 33 | bool active; 34 | #endregion 35 | 36 | #region Constructor 37 | public Tag(StringBuilder stringBuilder, string tag) 38 | { 39 | this.tag = tag; 40 | this.stringBuilder = stringBuilder; 41 | } 42 | #endregion 43 | 44 | #region Methods 45 | public void Set(bool required) 46 | { 47 | if (!active && required) 48 | Open(); 49 | if (active && !required) 50 | Close(); 51 | 52 | if (!required) 53 | value = null; 54 | } 55 | public void Set(bool required, string value) 56 | { 57 | switch (required) 58 | { 59 | case true when active && this.value != value && this.value != null: 60 | Close(); 61 | this.value = value; 62 | Open(); 63 | return; 64 | 65 | case false when !active: 66 | return; 67 | 68 | default: 69 | this.value = value; 70 | Set(required); 71 | break; 72 | } 73 | } 74 | 75 | void Open() 76 | { 77 | stringBuilder.Append(string.IsNullOrEmpty(value) ? $"<{tag}>" : $"<{tag}={value}>"); 78 | active = true; 79 | } 80 | void Close() 81 | { 82 | stringBuilder.Append($""); 83 | active = false; 84 | } 85 | #endregion 86 | } 87 | #endregion 88 | 89 | #region Fields 90 | readonly StringBuilder stringBuilder = new(); 91 | readonly Dictionary tags; 92 | readonly TextNode node; 93 | #endregion 94 | 95 | #region Constructors 96 | public TextBuilder(TextNode textNode) 97 | { 98 | tags = new Dictionary 99 | { 100 | { TagType.Bold, new Tag(stringBuilder, "b") }, 101 | { TagType.Italic, new Tag(stringBuilder, "i") }, 102 | { TagType.Underline, new Tag(stringBuilder, "u") }, 103 | { TagType.Strikethrough, new Tag(stringBuilder, "strikethrough") }, 104 | { TagType.Color, new Tag(stringBuilder, "color") }, 105 | { TagType.FontSize, new Tag(stringBuilder, "size") }, 106 | { TagType.FontWeight, new Tag(stringBuilder, "font-weight") }, 107 | { TagType.Indent, new Tag(stringBuilder, "indent") }, 108 | }; 109 | node = textNode; 110 | } 111 | #endregion 112 | 113 | #region Methods 114 | public string Build() 115 | { 116 | string text = node.characters; 117 | TextNode.Style baseStyle = node.style; 118 | int[] charOverrides = node.characterStyleOverrides; 119 | Dictionary styleTable = node.styleOverrideTable; 120 | LineType[] lineTypes = node.lineTypes; 121 | int[] lineIndents = node.lineIndentations ?? Array.Empty(); 122 | 123 | int textLength = text.Length; 124 | if (charOverrides.Length < textLength) 125 | { 126 | Array.Resize(ref charOverrides, textLength); 127 | for (int j = node.characterStyleOverrides.Length; j < textLength; j++) 128 | charOverrides[j] = 0; 129 | } 130 | 131 | int listLineIndex = 0; 132 | 133 | for (int i = 0, line = 0; i < textLength; i++) 134 | { 135 | char ch = text[i]; 136 | 137 | if (i == 0 || text[i - 1] == '\n') 138 | { 139 | if (i == 0) 140 | line = 0; 141 | else 142 | line++; 143 | 144 | int indentLevel = line < lineIndents.Length ? lineIndents[line] : 0; 145 | LineType lineType = line < lineTypes.Length ? lineTypes[line] : LineType.NONE; 146 | 147 | for (int s = 0; s < indentLevel; s++) 148 | tags[TagType.Indent].Set(lineType != LineType.NONE, (10 * indentLevel).ToString()); 149 | 150 | if (lineType is LineType.UNORDERED or LineType.NONE) listLineIndex = 0; 151 | if (lineType is LineType.ORDERED) stringBuilder.Append($"{++listLineIndex}. "); 152 | else if (lineType is LineType.UNORDERED) stringBuilder.Append("• "); 153 | } 154 | 155 | if (ch == '\n') 156 | { 157 | foreach (Tag tag in tags.Values) 158 | tag.Set(false); 159 | 160 | stringBuilder.Append('\n'); 161 | continue; 162 | } 163 | 164 | int styleOverrideId = i < charOverrides.Length ? charOverrides[i] : 0; 165 | TextNode.Style charStyle = styleTable.GetValueOrDefault(styleOverrideId, baseStyle); 166 | 167 | if (charStyle != null) 168 | { 169 | tags[TagType.Bold].Set(charStyle.fontWeight >= (int)FontWeight.Bold); 170 | tags[TagType.Italic].Set(charStyle.italic); 171 | tags[TagType.Underline].Set(charStyle.textDecoration is TextDecoration.UNDERLINE); 172 | tags[TagType.Strikethrough].Set(charStyle.textDecoration is TextDecoration.STRIKETHROUGH); 173 | 174 | SolidPaint paint = charStyle.fills?.OfType().FirstOrDefault(); 175 | tags[TagType.Color].Set(paint != null, paint == null ? null : "#" + ColorUtility.ToHtmlStringRGBA((Color)paint.color)); 176 | tags[TagType.FontWeight].Set(charStyle.fontWeight != (double)FontWeight.Regular, charStyle.fontWeight.ToString()); 177 | tags[TagType.FontSize].Set(true, charStyle.fontSize.ToString()); 178 | } 179 | 180 | stringBuilder.Append(ch); 181 | } 182 | 183 | foreach (Tag tag in tags.Values) 184 | tag.Set(false); 185 | 186 | return stringBuilder.ToString(); 187 | } 188 | #endregion 189 | } 190 | } -------------------------------------------------------------------------------- /Editor/Core/RichText/RichTextBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 63d2b4dce6024b989f78957756f9f22f 3 | timeCreated: 1743683644 -------------------------------------------------------------------------------- /Editor/Core/RootNodes.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Figma.Core 4 | { 5 | using Internals; 6 | 7 | internal class RootNodes 8 | { 9 | const int initialCollectionCapacity = 32; 10 | 11 | #region Fields 12 | readonly List canvases = new(initialCollectionCapacity); 13 | readonly List componentSets = new(initialCollectionCapacity); 14 | readonly List frames = new(initialCollectionCapacity); 15 | readonly List<(DefaultShapeNode, string hash)> elements = new(initialCollectionCapacity); 16 | #endregion 17 | 18 | #region Properties 19 | public IReadOnlyList Canvases => canvases; 20 | public IReadOnlyList ComponentSets => componentSets; 21 | public IReadOnlyList Frames => frames; 22 | public IReadOnlyList<(DefaultShapeNode node, string hash)> Elements => elements; 23 | #endregion 24 | 25 | #region Constructors 26 | public RootNodes(Data data, NodeMetadata nodeMetadata) 27 | { 28 | foreach (IBaseNodeMixin node in data.document.Flatten()) 29 | { 30 | switch (node) 31 | { 32 | case CanvasNode canvasNode: 33 | canvases.Add(canvasNode); 34 | break; 35 | 36 | case ComponentSetNode componentSetNode: 37 | componentSets.Add(componentSetNode); 38 | break; 39 | 40 | case FrameNode frameNode when node.parent is CanvasNode: 41 | frames.Add(frameNode); 42 | break; 43 | 44 | case DefaultShapeNode defaultShapeNode when nodeMetadata.GetTemplate(defaultShapeNode) is (_, { } template) && template.NotNullOrEmpty(): 45 | elements.Add((defaultShapeNode, template)); 46 | break; 47 | } 48 | } 49 | } 50 | #endregion 51 | } 52 | } -------------------------------------------------------------------------------- /Editor/Core/RootNodes.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 54369dcc94564417aee21cea7980006a 3 | timeCreated: 1733321003 -------------------------------------------------------------------------------- /Editor/Core/StylesPreprocessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 756f15f2e8e54ce6b41e0fdaa5aa7fe8 3 | timeCreated: 1742559104 -------------------------------------------------------------------------------- /Editor/Core/Uss.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eb2949d8c99346768cecb2a0968b2029 3 | timeCreated: 1732192985 -------------------------------------------------------------------------------- /Editor/Core/Uss/BaseUssStyle.cs: -------------------------------------------------------------------------------- 1 | using Figma.Internals; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Figma.Core.Uss 7 | { 8 | internal abstract class BaseUssStyle 9 | { 10 | #region Fields 11 | readonly List inherited = new(); 12 | #endregion 13 | 14 | #region Properties 15 | public string Name { get; set; } 16 | public PseudoClass PseudoClass { get; set; } 17 | public BaseUssStyle Target { get; set; } 18 | 19 | public List SubStyles { get; } = new(); 20 | public Dictionary Attributes { get; } = new(); 21 | public bool HasAttributes => Attributes.Count > 0; 22 | #endregion 23 | 24 | #region Constructors 25 | protected BaseUssStyle(string name) => Name = name; 26 | #endregion 27 | 28 | #region Methods 29 | public string BuildName() 30 | { 31 | string result = $".{Name}"; 32 | 33 | if (PseudoClass is not PseudoClass.None) 34 | result += $":{PseudoClass.ToString().ToLower()}"; 35 | 36 | if (Target == null) 37 | return result; 38 | 39 | result += " > "; 40 | 41 | if (Target.Name is not (nameof(UnityEngine.UIElements.VisualElement) or 42 | nameof(UnityEngine.UIElements.Button))) 43 | result += "."; 44 | 45 | result += Target.Name; 46 | 47 | return result; 48 | } 49 | 50 | public bool DoesInherit(BaseUssStyle style) => inherited.Contains(style); 51 | public void Inherit(BaseUssStyle component) 52 | { 53 | inherited.Add(component); 54 | component.Attributes.Keys.Where(key => Attributes.TryGetValue(key, out string value) && value == component.Attributes[key]) 55 | .ForEach(x => Attributes.Remove(x)); 56 | } 57 | public void Inherit(IReadOnlyCollection styles) 58 | { 59 | inherited.AddRange(styles); 60 | styles.SelectMany(style => style.Attributes.Where(keyValue => Attributes.TryGetValue(keyValue.Key, out string value) && value == style.Attributes[keyValue.Key])) 61 | .Select(x => x.Key) 62 | .ForEach(key => Attributes.Remove(key)); 63 | } 64 | public void Inherit(BaseUssStyle component, IReadOnlyCollection styles) 65 | { 66 | inherited.Add(component); 67 | inherited.AddRange(styles); 68 | 69 | List preserve = (from keyValue in component.Attributes 70 | from style in styles 71 | where style.Attributes.ContainsKey(keyValue.Key) && style.Attributes[keyValue.Key] != keyValue.Value 72 | select keyValue.Key).ToList(); 73 | 74 | component.Attributes.Keys.Where(key => Attributes.ContainsKey(key) && Attributes[key] == component.Attributes[key]).ForEach(key => Attributes.Remove(key)); 75 | styles.SelectMany(style => style.Attributes.Where(keyValue => Attributes.ContainsKey(keyValue.Key) && 76 | Attributes[keyValue.Key] == style.Attributes[keyValue.Key] && 77 | !preserve.Contains(keyValue.Key))) 78 | .Select(x => x.Key) 79 | .ForEach(x => Attributes.Remove(x)); 80 | } 81 | public string ResolveClassList(string component) => Attributes.Count > 0 ? $"{Name} {component}" : component; 82 | public string ResolveClassList(IEnumerable styles) => Attributes.Count > 0 ? $"{Name} {string.Join(" ", styles)}" : string.Join(" ", styles); 83 | public string ResolveClassList(string component, IEnumerable styles) => Attributes.Count > 0 ? $"{Name} {component} {string.Join(" ", styles)}" : $"{component} {string.Join(" ", styles)}"; 84 | public string ResolveClassList() => Attributes.Count > 0 ? Name : string.Empty; 85 | #endregion 86 | 87 | #region Support Methods 88 | protected bool Has(string name) => Attributes.ContainsKey(name); 89 | protected string Get(string name) => Attributes[name]; 90 | protected string GetDefault(string name, string defaultValue) => Attributes.ContainsKey(name) ? Attributes[name] : defaultValue; 91 | protected string Get1(string name, string group, int index) 92 | { 93 | if (Attributes.TryGetValue(group, out string groupValue)) 94 | { 95 | Length4Property length4 = groupValue; 96 | return length4[index]; 97 | } 98 | 99 | if (Attributes.TryGetValue(name, out string nameValue)) 100 | return nameValue; 101 | 102 | throw new NotSupportedException(); 103 | } 104 | protected string Get4(string name, params string[] names) 105 | { 106 | if (Attributes.TryGetValue(name, out string value)) 107 | return value; 108 | 109 | LengthProperty[] properties = new LengthProperty[4]; 110 | 111 | for (int i = 0; i < 4; ++i) 112 | properties[i] = Attributes.TryGetValue(names[i], out string indexedValue) ? indexedValue : new LengthProperty(Unit.Pixel); 113 | 114 | return new Length4Property(properties); 115 | } 116 | protected void Set(string name, string value) => Attributes[name] = value; 117 | protected void Set1(string name, string value, params string[] names) 118 | { 119 | Attributes[name] = value; 120 | 121 | for (int i = 0; i < 4; ++i) 122 | Attributes.Remove(names[i]); 123 | } 124 | protected void Set4(string name, string value, string group, int index) 125 | { 126 | if (Attributes.TryGetValue(group, out string item)) 127 | { 128 | Length4Property length4 = item; 129 | length4[index] = value; 130 | Set(group, length4); 131 | } 132 | else 133 | Set(name, value); 134 | } 135 | protected static string Url(string url) => $"url('{url}')"; 136 | protected static string Resource(string resource) => $"resource('{resource}')"; 137 | #endregion 138 | } 139 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/BaseUssStyle.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6a8ef63978cd415290c6cbe844cd7f9b 3 | timeCreated: 1732193154 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 049f021dc7f44bb389f9f132ed103b7c 3 | timeCreated: 1727946137 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/AssetProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Figma.Core.Uss 4 | { 5 | using Internals; 6 | 7 | /// 8 | /// Represents an asset in a Resources folder or represents an asset specified by a path, it can be expressed as either a relative path or an absolute path. 9 | /// 10 | internal struct AssetProperty 11 | { 12 | #region Fields 13 | readonly string url; 14 | readonly string resource; 15 | readonly Unit unit; 16 | #endregion 17 | 18 | #region Constructors 19 | AssetProperty(Unit unit) 20 | { 21 | url = null; 22 | resource = null; 23 | this.unit = unit; 24 | } 25 | AssetProperty(string value) 26 | { 27 | url = null; 28 | resource = null; 29 | unit = default; 30 | 31 | if (value.StartsWith(nameof(url))) url = value; 32 | else if (value.StartsWith(nameof(resource))) resource = value; 33 | else throw new NotSupportedException(); 34 | } 35 | #endregion 36 | 37 | #region Operators 38 | public static implicit operator AssetProperty(Unit value) => new(value); 39 | public static implicit operator AssetProperty(string value) => Enum.TryParse(value, true, out Unit unit) ? new AssetProperty(unit) : new AssetProperty(value); 40 | public static implicit operator string(AssetProperty value) 41 | { 42 | if (value.url.NotNullOrEmpty()) return value.url; 43 | if (value.resource.NotNullOrEmpty()) return value.resource; 44 | 45 | return value.unit switch 46 | { 47 | Unit.None => "none", 48 | Unit.Initial => "initial", 49 | _ => throw new ArgumentException(nameof(value)) 50 | }; 51 | } 52 | #endregion 53 | } 54 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/AssetProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b1d01eee68b444c88e74da4382b53373 3 | timeCreated: 1727946243 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/ColorProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Figma.Core.Uss 4 | { 5 | using Internals; 6 | 7 | /// 8 | /// Represents a color. You can define a color with a #hexadecimal code, the rgb() or rgba() function, or a color keyword (for example, blue or transparent). 9 | /// 10 | internal readonly struct ColorProperty 11 | { 12 | #region Fields 13 | readonly string rgba; 14 | readonly string rgb; 15 | readonly string hex; 16 | readonly string name; 17 | #endregion 18 | 19 | #region Constructors 20 | internal ColorProperty(RGBA color, Double? opacity = 1, float alphaMult = 1) 21 | { 22 | rgba = $"rgba({(byte)(color.r * 255.0f)},{(byte)(color.g * 255.0f)},{(byte)(color.b * 255.0f)},{(color.a * (opacity ?? alphaMult)).ToString("F2", Const.Culture).Replace(".00", string.Empty)})"; 23 | rgb = null; 24 | hex = null; 25 | name = null; 26 | } 27 | ColorProperty(string value) 28 | { 29 | rgba = null; 30 | rgb = null; 31 | hex = null; 32 | name = null; 33 | 34 | if (value.StartsWith(nameof(rgba))) rgba = value; 35 | else if (value.StartsWith(nameof(rgb))) rgb = value; 36 | else if (value.StartsWith('#')) hex = value; 37 | else name = value; 38 | } 39 | #endregion 40 | 41 | #region Operators 42 | public static implicit operator ColorProperty(Unit _) => new(); 43 | public static implicit operator ColorProperty(RGBA value) => new(value); 44 | public static implicit operator ColorProperty(string value) => new(value); 45 | public static implicit operator string(ColorProperty value) 46 | { 47 | if (value.rgba.NotNullOrEmpty()) return value.rgba; 48 | if (value.rgb.NotNullOrEmpty()) return value.rgb; 49 | if (value.hex.NotNullOrEmpty()) return value.hex; 50 | if (value.name.NotNullOrEmpty()) return value.name; 51 | 52 | return "initial"; 53 | } 54 | public override string ToString() => this; 55 | #endregion 56 | } 57 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/ColorProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 11225bd9ebe040f49fab329a81bc3f88 3 | timeCreated: 1727946232 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/CursorProperty.cs: -------------------------------------------------------------------------------- 1 | namespace Figma.Core.Uss 2 | { 3 | internal struct CursorProperty 4 | { 5 | #region Operators 6 | public static implicit operator CursorProperty(string _) => new(); 7 | public static implicit operator string(CursorProperty _) => null; 8 | #endregion 9 | } 10 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/CursorProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 688b2f12fd56420486ab60eaa3f520c7 3 | timeCreated: 1727946311 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/DurationProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Figma.Internals; 3 | 4 | namespace Figma.Core.Uss 5 | { 6 | /// 7 | /// Represents a duration value from Figma API. 8 | /// 9 | internal readonly struct DurationProperty 10 | { 11 | #region Fields 12 | readonly double value; 13 | readonly TimeUnit unit; 14 | #endregion 15 | 16 | #region Constructors 17 | internal DurationProperty(TimeUnit unit) 18 | { 19 | value = 0; 20 | this.unit = unit; 21 | } 22 | internal DurationProperty(double value, TimeUnit unit) 23 | { 24 | this.value = value; 25 | this.unit = unit; 26 | } 27 | #endregion 28 | 29 | #region Operators 30 | public static implicit operator DurationProperty(TimeUnit value) => new(0, value); 31 | public static implicit operator DurationProperty(double? value) => new(value!.Value, TimeUnit.Millisecond); 32 | public static implicit operator DurationProperty(double value) => new(value, TimeUnit.Millisecond); 33 | public static implicit operator DurationProperty(string value) 34 | { 35 | if (Enum.TryParse(value, true, out TimeUnit unit)) return new DurationProperty(unit); 36 | if (value.ToLower(Const.Culture).Contains("ms")) return new DurationProperty(double.Parse(value.ToLower(Const.Culture).Replace("ms", string.Empty), Const.Culture), TimeUnit.Millisecond); 37 | if (value.ToLower(Const.Culture).Contains("s")) return new DurationProperty(double.Parse(value.ToLower(Const.Culture).Replace("s", string.Empty), Const.Culture), TimeUnit.Second); 38 | 39 | return default; 40 | } 41 | public static implicit operator string(DurationProperty value) 42 | { 43 | return value.unit switch 44 | { 45 | TimeUnit.Default => $"0ms", 46 | TimeUnit.Millisecond => $"{value.value.ToString("F2", Const.Culture).Replace(".00", string.Empty)}ms", 47 | TimeUnit.Second => $"{value.value.ToString("F2", Const.Culture).Replace(".00", string.Empty)}s", 48 | _ => throw new ArgumentException(nameof(value)) 49 | }; 50 | } 51 | 52 | public static DurationProperty operator +(DurationProperty a) => a; 53 | public static DurationProperty operator -(DurationProperty a) => new(-a.value, a.unit); 54 | public static DurationProperty operator +(DurationProperty a, double b) => new(a.value + b, a.unit); 55 | public static DurationProperty operator -(DurationProperty a, double b) => new(a.value - b, a.unit); 56 | 57 | public static bool operator ==(DurationProperty a, TimeUnit b) => a.unit == b; 58 | public static bool operator !=(DurationProperty a, TimeUnit b) => a.unit != b; 59 | 60 | public override bool Equals(object obj) => obj is DurationProperty property && value == property.value && unit == property.unit; 61 | public override int GetHashCode() => HashCode.Combine(value, unit); 62 | public override string ToString() => this; 63 | #endregion 64 | } 65 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/DurationProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f01b58e2e2c44e75b07f54061e244f90 3 | timeCreated: 1732271697 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/EnumProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace Figma.Core.Uss 5 | { 6 | #pragma warning disable CS0660, CS0661 7 | internal struct EnumProperty where T : struct, Enum 8 | #pragma warning restore CS0660, CS0661 9 | { 10 | // ReSharper disable StaticMemberInGenericType 11 | static readonly Regex enumParserRegexString = new("(?([a-z]+\\-?))", RegexOptions.Compiled); 12 | static readonly Regex enumParserRegexValue = new("(?([A-Z][a-z]+)?)", RegexOptions.Compiled); 13 | // ReSharper restore StaticMemberInGenericType 14 | 15 | #region Fields 16 | T value; 17 | readonly Unit unit; 18 | #endregion 19 | 20 | #region Constructors 21 | EnumProperty(T value) 22 | { 23 | this.value = value; 24 | unit = Unit.None; 25 | } 26 | EnumProperty(Unit unit) 27 | { 28 | value = default; 29 | this.unit = unit; 30 | } 31 | #endregion 32 | 33 | #region Operators 34 | public static implicit operator EnumProperty(Unit unit) => new(unit); 35 | public static implicit operator EnumProperty(T value) => new(value); 36 | public static implicit operator EnumProperty(string value) => Enum.TryParse(enumParserRegexString.Replace(value, "${name}").Replace("-", string.Empty), true, out T result) ? new EnumProperty(result) : default; 37 | public static implicit operator string(EnumProperty value) => value.unit == Unit.None ? enumParserRegexValue.Replace(value.value.ToString(), "${name}-").ToLower().TrimEnd('-') : "initial"; 38 | 39 | public static bool operator ==(EnumProperty a, T b) => a.value.Equals(b); 40 | public static bool operator !=(EnumProperty a, T b) => !a.value.Equals(b); 41 | 42 | public override string ToString() => this; 43 | #endregion 44 | } 45 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/EnumProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 911a38bb38a64ff98a53324b53da101b 3 | timeCreated: 1727946257 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/FlexProperty.cs: -------------------------------------------------------------------------------- 1 | namespace Figma.Core.Uss 2 | { 3 | internal struct FlexProperty 4 | { 5 | #region Operators 6 | public static implicit operator FlexProperty(string _) => new(); 7 | public static implicit operator string(FlexProperty _) => null; 8 | #endregion 9 | } 10 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/FlexProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c6e36b8acd8548b0add9ddf1a37509b0 3 | timeCreated: 1727946301 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/IntegerProperty.cs: -------------------------------------------------------------------------------- 1 | namespace Figma.Core.Uss 2 | { 3 | using Internals; 4 | 5 | /// 6 | /// Represents a whole number. 7 | /// 8 | internal struct IntegerProperty 9 | { 10 | #region Fields 11 | readonly int value; 12 | #endregion 13 | 14 | #region Constructors 15 | IntegerProperty(int value) => this.value = value; 16 | #endregion 17 | 18 | #region Operators 19 | public static implicit operator IntegerProperty(int? value) => new(value!.Value); 20 | public static implicit operator IntegerProperty(int value) => new(value); 21 | public static implicit operator IntegerProperty(string value) => new(int.Parse(value)); 22 | public static implicit operator string(IntegerProperty value) => value.value.ToString(Const.Culture); 23 | 24 | public static IntegerProperty operator +(IntegerProperty a) => a; 25 | public static IntegerProperty operator -(IntegerProperty a) => new(-a.value); 26 | public static IntegerProperty operator +(IntegerProperty a, int b) => new(a.value + b); 27 | public static IntegerProperty operator -(IntegerProperty a, int b) => new(a.value - b); 28 | #endregion 29 | } 30 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/IntegerProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a466380f063f4ec98d082d2ec7cd1f20 3 | timeCreated: 1727946212 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/LayoutDouble4.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Figma.Core.Uss 4 | { 5 | struct LayoutDouble4 6 | { 7 | #region Fields 8 | public double top; 9 | public double right; 10 | public double bottom; 11 | public double left; 12 | #endregion 13 | 14 | #region Methods 15 | public LayoutDouble4(double top, double right, double bottom, double left) 16 | { 17 | this.top = top; 18 | this.right = right; 19 | this.bottom = bottom; 20 | this.left = left; 21 | } 22 | public LayoutDouble4(double value) 23 | { 24 | top = value; 25 | right = value; 26 | bottom = value; 27 | left = value; 28 | } 29 | 30 | public LayoutDouble4 OnlyPositiveValues() => new(top > UssStyle.tolerance ? top : 0.0, right > UssStyle.tolerance ? right : 0.0, bottom > UssStyle.tolerance ? bottom : 0.0, left > UssStyle.tolerance ? left : 0.0); 31 | public LayoutDouble4 OnlyNegativeValues() => new(top < UssStyle.tolerance ? top : 0.0, right < UssStyle.tolerance ? right : 0.0, bottom < UssStyle.tolerance ? bottom : 0.0, left < UssStyle.tolerance ? left : 0.0); 32 | 33 | public Length4Property ToLength4Property() => new[] { top, right, bottom, left }; 34 | public bool Any() => Math.Abs(top) > UssStyle.tolerance || Math.Abs(right) > UssStyle.tolerance || Math.Abs(bottom) > UssStyle.tolerance || Math.Abs(left) > UssStyle.tolerance; 35 | 36 | public static LayoutDouble4 operator +(LayoutDouble4 a, LayoutDouble4 b) => new(a.top + b.top, a.right + b.right, a.bottom + b.bottom, a.left + b.left); 37 | public static LayoutDouble4 operator -(LayoutDouble4 a, LayoutDouble4 b) => new(a.top - b.top, a.right - b.right, a.bottom - b.bottom, a.left - b.left); 38 | public static LayoutDouble4 operator -(LayoutDouble4 a) => new(-a.top, -a.right, -a.bottom, -a.left); 39 | public static LayoutDouble4 operator *(LayoutDouble4 a, double k) => new(a.top * k, a.right * k, a.bottom * k, a.left * k); 40 | #endregion 41 | } 42 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/LayoutDouble4.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c58a19efd2f4860a12c1aca6d120164 3 | timeCreated: 1743523469 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/Length2Property.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Figma.Core.Uss 5 | { 6 | internal readonly struct Length2Property 7 | { 8 | #region Fields 9 | readonly Unit unit; 10 | readonly LengthProperty[] properties; 11 | #endregion 12 | 13 | #region Properties 14 | internal LengthProperty this[int index] { get => properties[index]; set => properties[index] = value; } 15 | #endregion 16 | 17 | #region Constructors 18 | internal Length2Property(Unit unit) 19 | { 20 | this.unit = unit; 21 | properties = new LengthProperty[] { new(unit), new(unit) }; 22 | } 23 | internal Length2Property(LengthProperty[] properties) 24 | { 25 | unit = Unit.None; 26 | this.properties = properties; 27 | } 28 | #endregion 29 | 30 | #region Operators 31 | public static implicit operator Length2Property(Unit unit) => new(unit); 32 | public static implicit operator Length2Property(Double? value) => new(new LengthProperty[] { value!.Value }); 33 | public static implicit operator Length2Property(Double value) => new(new LengthProperty[] { value }); 34 | public static implicit operator Length2Property(Double[] values) 35 | { 36 | LengthProperty[] properties = new LengthProperty[values.Length]; 37 | for (int i = 0; i < values.Length; i++) properties[i] = values[i]; 38 | return new Length2Property(properties); 39 | } 40 | public static implicit operator Length2Property(string value) 41 | { 42 | string[] values = value.Split(" ", StringSplitOptions.RemoveEmptyEntries); 43 | LengthProperty[] properties = new LengthProperty[values.Length]; 44 | for (int i = 0; i < values.Length; i++) properties[i] = values[i]; 45 | return new Length2Property(properties); 46 | } 47 | public static implicit operator string(Length2Property value) 48 | { 49 | if (value is { unit: Unit.None, properties: not null }) 50 | { 51 | string[] values = new string[value.properties.Length]; 52 | for (int i = 0; i < values.Length; i++) values[i] = value.properties[i]; 53 | return string.Join(" ", values); 54 | } 55 | 56 | return new LengthProperty(value.unit); 57 | } 58 | 59 | public static Length2Property operator +(Length2Property a) => a; 60 | public static Length2Property operator -(Length2Property a) => new(a.properties.Select(x => -x).ToArray()); 61 | public static Length2Property operator +(Length2Property a, Double b) => new(a.properties.Select(x => x + b).ToArray()); 62 | public static Length2Property operator -(Length2Property a, Double b) => new(a.properties.Select(x => x - b).ToArray()); 63 | #endregion 64 | } 65 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/Length2Property.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ac3787c7e7634fc9983559df65387add 3 | timeCreated: 1738319788 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/Length4Property.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Figma.Core.Uss 5 | { 6 | internal readonly struct Length4Property 7 | { 8 | #region Fields 9 | readonly Unit unit; 10 | readonly LengthProperty[] properties; 11 | #endregion 12 | 13 | #region Properties 14 | internal LengthProperty this[int index] { get => properties[index]; set => properties[index] = value; } 15 | #endregion 16 | 17 | #region Constructors 18 | internal Length4Property(Unit unit) 19 | { 20 | this.unit = unit; 21 | properties = new LengthProperty[] { new(unit), new(unit), new(unit), new(unit) }; 22 | } 23 | internal Length4Property(LengthProperty[] properties) 24 | { 25 | unit = Unit.None; 26 | this.properties = properties; 27 | } 28 | #endregion 29 | 30 | #region Operators 31 | public static implicit operator Length4Property(Unit unit) => new(unit); 32 | public static implicit operator Length4Property(double? value) => new(new LengthProperty[] { value!.Value }); 33 | public static implicit operator Length4Property(double value) => new(new LengthProperty[] { value }); 34 | public static implicit operator Length4Property(double[] values) => new(values.Select(x => (LengthProperty)x).ToArray()); 35 | public static implicit operator Length4Property(string value) => new(value.Split(" ", StringSplitOptions.RemoveEmptyEntries).Select(x => (LengthProperty)x).ToArray()); 36 | public static implicit operator string(Length4Property value) => value is { unit: Unit.None, properties: not null } ? string.Join(" ", value.properties.Select(p => (string)p)) : new LengthProperty(value.unit); 37 | 38 | public static Length4Property operator +(Length4Property a) => a; 39 | public static Length4Property operator -(Length4Property a) => new(a.properties.Select(x => -x).ToArray()); 40 | public static Length4Property operator +(Length4Property a, double b) => new(a.properties.Select(x => x + b).ToArray()); 41 | public static Length4Property operator -(Length4Property a, double b) => new(a.properties.Select(x => x - b).ToArray()); 42 | #endregion 43 | } 44 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/Length4Property.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 22ebc27065094f06894a5301834db528 3 | timeCreated: 1727946288 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/LengthProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Figma.Core.Uss 4 | { 5 | using Internals; 6 | 7 | /// 8 | /// Represents a distance value. 9 | /// 10 | internal readonly struct LengthProperty 11 | { 12 | #region Fields 13 | readonly double value; 14 | readonly Unit unit; 15 | #endregion 16 | 17 | #region Constructors 18 | internal LengthProperty(Unit unit) 19 | { 20 | value = 0; 21 | this.unit = unit; 22 | } 23 | internal LengthProperty(double value, Unit unit) 24 | { 25 | this.value = value; 26 | this.unit = unit; 27 | } 28 | #endregion 29 | 30 | #region Operators 31 | public static implicit operator LengthProperty(Unit value) => new(0, value); 32 | public static implicit operator LengthProperty(double? value) => new(value!.Value, Unit.Pixel); 33 | public static implicit operator LengthProperty(double value) => new(value, Unit.Pixel); 34 | public static implicit operator LengthProperty(string value) 35 | { 36 | if (Enum.TryParse(value, true, out Unit unit)) return new LengthProperty(unit); 37 | if (value.Contains("px")) return new LengthProperty(double.Parse(value.ToLower().Replace("px", string.Empty), Const.Culture), Unit.Pixel); 38 | if (value.Contains("deg")) return new LengthProperty(double.Parse(value.ToLower().Replace("deg", string.Empty), Const.Culture), Unit.Degrees); 39 | if (value.Contains('%')) return new LengthProperty(double.Parse(value.Replace("%", string.Empty), Const.Culture), Unit.Percent); 40 | 41 | return default; 42 | } 43 | public static implicit operator string(LengthProperty value) 44 | { 45 | return value.unit switch 46 | { 47 | Unit.Pixel => value.value == 0.0 ? "0" : $"{(int)Math.Round(value.value)}px", 48 | Unit.Degrees => value.value == 0.0 ? "0" : $"{value.value.ToString("F2", Const.Culture).Replace(".00", string.Empty)}deg", 49 | Unit.Percent => value.value == 0.0 ? "0" : $"{value.value.ToString("F2", Const.Culture).Replace(".00", string.Empty)}%", 50 | Unit.Auto => "auto", 51 | Unit.None => "none", 52 | Unit.Initial => "initial", 53 | Unit.Default => "0", 54 | _ => throw new ArgumentException(nameof(value)) 55 | }; 56 | } 57 | 58 | public static LengthProperty operator +(LengthProperty a) => a; 59 | public static LengthProperty operator -(LengthProperty a) => new(-a.value, a.unit); 60 | public static LengthProperty operator +(LengthProperty a, double b) => new(a.value + b, a.unit); 61 | public static LengthProperty operator -(LengthProperty a, double b) => new(a.value - b, a.unit); 62 | 63 | public static bool operator ==(LengthProperty a, Unit b) => a.unit == b; 64 | public static bool operator !=(LengthProperty a, Unit b) => a.unit != b; 65 | 66 | public override bool Equals(object obj) => obj is LengthProperty property && value == property.value && unit == property.unit; 67 | public override int GetHashCode() => HashCode.Combine(value, unit); 68 | public override string ToString() => this; 69 | #endregion 70 | } 71 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/LengthProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 50407f2db68e4ab3ac1c3fe00a5b98f7 3 | timeCreated: 1727946151 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/NumberProperty.cs: -------------------------------------------------------------------------------- 1 | namespace Figma.Core.Uss 2 | { 3 | using Internals; 4 | 5 | /// 6 | /// Represents either an integer or a number with a fractional component. 7 | /// 8 | internal struct NumberProperty 9 | { 10 | #region Fields 11 | readonly double value; 12 | #endregion 13 | 14 | #region Constructors 15 | NumberProperty(double value) => this.value = value; 16 | #endregion 17 | 18 | #region Operators 19 | public static implicit operator NumberProperty(double? value) => new(value!.Value); 20 | public static implicit operator NumberProperty(double value) => new(value); 21 | public static implicit operator NumberProperty(string value) => new(double.Parse(value, Const.Culture)); 22 | public static implicit operator string(NumberProperty value) => value.value.ToString("F2", Const.Culture).Replace(".00", string.Empty); 23 | 24 | public static NumberProperty operator +(NumberProperty a) => a; 25 | public static NumberProperty operator -(NumberProperty a) => new(-a.value); 26 | public static NumberProperty operator +(NumberProperty a, double b) => new(a.value + b); 27 | public static NumberProperty operator -(NumberProperty a, double b) => new(a.value - b); 28 | #endregion 29 | } 30 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/NumberProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 674b330b5aea49318570f61fabe5e9cb 3 | timeCreated: 1727946179 -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/ShadowProperty.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace Figma.Core.Uss 4 | { 5 | internal struct ShadowProperty 6 | { 7 | static readonly Regex regex = new(@"(?\d+[px]*)\s+(?\d+[px]*)\s+(?\d+[px]*)\s+(?(rgba\([\d,\.\s]+\))|#\w{2,8}|[^#][\w-]+)"); 8 | 9 | #region Fields 10 | readonly LengthProperty offsetHorizontal; 11 | readonly LengthProperty offsetVertical; 12 | readonly LengthProperty blurRadius; 13 | readonly ColorProperty color; 14 | #endregion 15 | 16 | #region Constructors 17 | internal ShadowProperty(LengthProperty offsetHorizontal, LengthProperty offsetVertical, LengthProperty blurRadius, ColorProperty color) 18 | { 19 | this.offsetHorizontal = offsetHorizontal; 20 | this.offsetVertical = offsetVertical; 21 | this.blurRadius = blurRadius; 22 | this.color = color; 23 | } 24 | ShadowProperty(string value) 25 | { 26 | Match match = regex.Match(value); 27 | offsetHorizontal = match.Groups[nameof(offsetHorizontal)].Value; 28 | offsetVertical = match.Groups[nameof(offsetVertical)].Value; 29 | blurRadius = match.Groups[nameof(blurRadius)].Value; 30 | color = match.Groups[nameof(color)].Value; 31 | } 32 | #endregion 33 | 34 | #region Operators 35 | public static implicit operator ShadowProperty(string value) => new(value); 36 | public static implicit operator string(ShadowProperty value) => $"{value.offsetHorizontal} {value.offsetVertical} {value.blurRadius} {value.color}"; 37 | #endregion 38 | } 39 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/Properties/ShadowProperty.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c3584d21c614453fa132bc171f4420eb 3 | timeCreated: 1727946275 -------------------------------------------------------------------------------- /Editor/Core/Uss/StyleSlot.cs: -------------------------------------------------------------------------------- 1 | namespace Figma.Core.Uss 2 | { 3 | using Internals; 4 | 5 | internal class StyleSlot : Style 6 | { 7 | #region Fields 8 | public bool Text { get; } 9 | public string Slot { get; } 10 | #endregion 11 | 12 | #region Constructors 13 | public StyleSlot(bool text, string slot, Style style) 14 | { 15 | Text = text; 16 | Slot = slot; 17 | styleType = style.styleType; 18 | key = style.key; 19 | name = style.name; 20 | description = style.description; 21 | } 22 | #endregion 23 | 24 | #region Methods 25 | public override string ToString() => $"text={Text} slot={Slot} styleType={styleType} key={key} name={name} description={description}"; 26 | #endregion 27 | } 28 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/StyleSlot.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7cb6785b010e457392b9a283f2cd0e62 3 | timeCreated: 1727945310 -------------------------------------------------------------------------------- /Editor/Core/Uss/UssStyle.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8d258033e1644b1fa80fcdf1fb668783 3 | timeCreated: 1727945365 -------------------------------------------------------------------------------- /Editor/Core/Uss/UssWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Figma.Core.Uss 9 | { 10 | using Internals; 11 | 12 | internal class UssWriter : IDisposable, IAsyncDisposable 13 | { 14 | #region Fields 15 | readonly string rootDirectory; 16 | readonly StreamWriter stream; 17 | int count; 18 | #endregion 19 | 20 | #region Constructors 21 | public UssWriter(string rootDirectory, string path) 22 | { 23 | this.rootDirectory = rootDirectory; 24 | stream = new StreamWriter(Path = path, false, Encoding.UTF8, 1024); 25 | } 26 | #endregion 27 | 28 | #region Properties 29 | public string Path { get; } 30 | #endregion 31 | 32 | #region Methods 33 | public void Write(BaseUssStyle style) 34 | { 35 | if (!style.HasAttributes) 36 | return; 37 | 38 | if (count > 0) 39 | { 40 | stream.WriteLine(); 41 | stream.WriteLine(); 42 | } 43 | 44 | stream.Write(style.BuildName()); 45 | stream.WriteLine(" {"); 46 | 47 | foreach ((string key, string value) in style.Attributes) 48 | { 49 | switch (key) 50 | { 51 | case "background-image" when value.Contains("url"): 52 | // Getting a relative path to image to keep the reference. 53 | // Since images are located in a parent of a parent directory. 54 | // Assets/Images directory and frames are in Assets/Frames/CanvasName/ 55 | // We need to calculate a relative path and the only way of doing this 56 | // is on write operation, since UssStyle is not aware of the path, where it 57 | // would be written. 58 | stream.WriteLine($"\t{key}: url('{System.IO.Path.GetRelativePath(System.IO.Path.GetDirectoryName(Path), PathExtensions.CombinePath(rootDirectory, value[5..^2]))?.Replace('\\', '/')}');"); 59 | continue; 60 | 61 | default: 62 | stream.WriteLine($"\t{key}: {value};"); 63 | break; 64 | } 65 | } 66 | 67 | stream.Write("}"); 68 | 69 | count++; 70 | 71 | Write(style.SubStyles); 72 | } 73 | public void Write(IEnumerable styles) => styles.OrderBy(x => x.Name).ForEach(Write); 74 | 75 | void IDisposable.Dispose() => stream?.Dispose(); 76 | async ValueTask IAsyncDisposable.DisposeAsync() 77 | { 78 | if (stream != null) 79 | await stream.DisposeAsync(); 80 | } 81 | #endregion 82 | } 83 | } -------------------------------------------------------------------------------- /Editor/Core/Uss/UssWriter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d732fb08b4e246c6bce62bad8551f8f8 3 | timeCreated: 1727945385 -------------------------------------------------------------------------------- /Editor/Core/Uxml.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b264c8b3ae0846dc87a6a96ccb5c5dbf 3 | timeCreated: 1733218052 -------------------------------------------------------------------------------- /Editor/Core/Uxml/UxmlBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using UnityAsyncAwaitUtil; 6 | using UnityEngine; 7 | 8 | namespace Figma.Core.Uxml 9 | { 10 | using Internals; 11 | using static Internals.Const; 12 | using static Internals.Extensions; 13 | using static Internals.PathExtensions; 14 | 15 | internal class UxmlBuilder 16 | { 17 | #region Fields 18 | readonly Data data; 19 | readonly NodeMetadata nodeMetadata; 20 | readonly string globalUssFilePath; 21 | readonly Func getClassList; 22 | #endregion 23 | 24 | #region Constructors 25 | public UxmlBuilder(Data data, NodeMetadata nodeMetadata, string globalUssFilePath, Func getClassList) 26 | { 27 | this.data = data; 28 | this.nodeMetadata = nodeMetadata; 29 | this.globalUssFilePath = globalUssFilePath; 30 | this.getClassList = getClassList; 31 | } 32 | #endregion 33 | 34 | #region Methods 35 | public void CreateDocument(string directory, string fileName, DocumentNode documentNode, IReadOnlyDictionary> framesPaths) 36 | { 37 | string CreateTemplateName(string path) => RemoveExtension(path).Replace('/', '-'); 38 | 39 | using UxmlWriter writer = new(directory, fileName); 40 | 41 | writer.WriteUssStyleReference(GetRelativePath(writer.filePath, globalUssFilePath)); 42 | writer.StartElement(documentNode, getClassList(documentNode), nodeMetadata.GetElementType(documentNode)); 43 | 44 | foreach (string templatePath in framesPaths.SelectMany(framesPath => framesPath.Value)) 45 | { 46 | string path = GetRelativePath(writer.filePath, templatePath); 47 | writer.WriteTemplate(CreateTemplateName(path), path); 48 | } 49 | 50 | foreach ((string name, IReadOnlyList scope) in framesPaths) 51 | { 52 | if (scope.Count == 0) 53 | continue; 54 | 55 | writer.StartElement(nameof(UnityEngine.UIElements.VisualElement), ("class", "unity-viewport"), (nameof(name), name)); 56 | foreach (string path in scope) 57 | { 58 | string frameName = Path.GetFileNameWithoutExtension(path); 59 | string templateName = CreateTemplateName(GetRelativePath(writer.filePath, path)); 60 | writer.WriteInstance(frameName, templateName, Uss.UssStyle.viewportClass.Name); 61 | } 62 | writer.EndElement(); 63 | } 64 | 65 | writer.EndElement(); 66 | } 67 | public string CreateFrame(string directory, string[] ussStyleFilesPath, IReadOnlyDictionary templates, DefaultFrameNode frameNode) 68 | { 69 | using UxmlWriter writer = new(directory, frameNode.name); 70 | 71 | WriteStyles(ussStyleFilesPath, writer); 72 | 73 | foreach ((string templateName, string templatePath) in templates) 74 | writer.WriteTemplate(templateName, GetRelativePath(writer.filePath, templatePath)); 75 | 76 | WriteNodesRecursively(frameNode, writer); 77 | 78 | return writer.filePath; 79 | } 80 | public string CreateComponentSet(string directory, string[] ussStyleFilesPath, ComponentSetNode componentSetNode) 81 | { 82 | using UxmlWriter writer = new(directory, componentSetNode.name); 83 | 84 | WriteStyles(ussStyleFilesPath, writer); 85 | WriteNodesRecursively(componentSetNode, writer); 86 | 87 | return writer.filePath; 88 | } 89 | public string CreateElement(string directory, string[] ussStyleFilesPath, DefaultShapeNode node, string template) 90 | { 91 | (bool isHash, string hashedTemplate) = nodeMetadata.GetTemplate(node); 92 | using UxmlWriter writer = new(directory, isHash ? hashedTemplate : template); 93 | 94 | WriteStyles(ussStyleFilesPath, writer); 95 | 96 | if (node is DefaultFrameNode parent) 97 | foreach (SceneNode child in parent.children) 98 | WriteNodesRecursively(child, writer); 99 | 100 | return writer.filePath; 101 | } 102 | 103 | void WriteNodesRecursively(BaseNode node, UxmlWriter uxml, bool isComponent = false) 104 | { 105 | void WriteCanvasNode(CanvasNode canvasNode, UxmlWriter writer) 106 | { 107 | writer.StartElement(canvasNode, getClassList(canvasNode), nodeMetadata.GetElementType(canvasNode)); 108 | canvasNode.children.ForEach(child => WriteNodesRecursively(child, writer)); 109 | writer.EndElement(); 110 | } 111 | void WriteSectionNode(SectionNode sectionNode, UxmlWriter writer) 112 | { 113 | writer.StartElement(sectionNode, getClassList(sectionNode), nodeMetadata.GetElementType(sectionNode)); 114 | sectionNode.children.ForEach(child => WriteNodesRecursively(child, writer)); 115 | writer.EndElement(); 116 | 117 | throw new NotImplementedException(nameof(WriteSectionNode)); 118 | } 119 | void WriteSliceNode(SliceNode sliceNode, UxmlWriter writer) 120 | { 121 | writer.StartElement(sliceNode, getClassList(sliceNode), nodeMetadata.GetElementType(sliceNode)); 122 | writer.EndElement(); 123 | } 124 | void WriteTextNode(TextNode textNode, UxmlWriter writer) 125 | { 126 | writer.StartElement(textNode, getClassList(textNode), nodeMetadata.GetElementType(textNode)); 127 | 128 | string text = textNode.style.textCase switch 129 | { 130 | TextCase.UPPER => textNode.characters.ToUpper(Culture), 131 | TextCase.LOWER => textNode.characters.ToLower(Culture), 132 | _ => textNode.characters 133 | }; 134 | writer.xmlWriter.WriteAttributeString(nameof(text), text); 135 | writer.EndElement(); 136 | } 137 | void WriteDefaultFrameNode(DefaultFrameNode defaultFrameNode, UxmlWriter writer) 138 | { 139 | WriteDefaultShapeNode(defaultFrameNode, writer, false); 140 | defaultFrameNode.children.ForEach(child => WriteNodesRecursively(child, writer, isComponent)); 141 | writer.EndElement(); 142 | } 143 | void WriteDefaultShapeNode(DefaultShapeNode defaultShapeNode, UxmlWriter writer, bool closeElement = true) 144 | { 145 | string tooltip = null; 146 | if (nodeMetadata.GetTemplate(defaultShapeNode) is (var hash, { } template) && template.NotNullOrEmpty()) 147 | tooltip = hash ? template : null; 148 | 149 | writer.StartElement(defaultShapeNode, getClassList(defaultShapeNode), nodeMetadata.GetElementType(defaultShapeNode)); 150 | if (tooltip.NotNullOrEmpty()) 151 | writer.xmlWriter.WriteAttributeString(nameof(tooltip), tooltip!); // Use tooltip as a storage for hash template name 152 | if (closeElement) 153 | writer.EndElement(); 154 | } 155 | void WriteInstanceNode(InstanceNode instanceNode, UxmlWriter writer) 156 | { 157 | if (data.components.TryGetValue(instanceNode.componentId, out Component component) && !component.remote && 158 | !string.IsNullOrEmpty(component.componentSetId) && 159 | data.componentSets.TryGetValue(component.componentSetId, out Component target) && !target.remote) 160 | { 161 | string componentSetName = target.name; 162 | string classList = getClassList(instanceNode); 163 | 164 | writer.WriteInstance(instanceNode.name, componentSetName, classList); 165 | } 166 | else 167 | { 168 | // Since this code only runs from Parallel, outside of Unity scope 169 | // We cannot use Debug.Log() without returning to Unity's thread 170 | SyncContextUtil.UnitySynchronizationContext.Post(_ => Debug.LogWarning(BuildTargetMessage($"Target {nameof(Component)} for node", instanceNode.GetFullPath(), "is not found")), null); 171 | WriteDefaultFrameNode(instanceNode, writer); 172 | } 173 | } 174 | 175 | if (!node.IsVisible() || (!nodeMetadata.EnabledInHierarchy(node) && node is not ComponentSetNode && !isComponent)) 176 | return; 177 | 178 | if (node is CanvasNode canvas) WriteCanvasNode(canvas, uxml); 179 | if (node is FrameNode frame) WriteDefaultFrameNode(frame, uxml); 180 | if (node is GroupNode group) WriteDefaultFrameNode(group, uxml); 181 | if (node is ComponentSetNode componentSet) WriteDefaultFrameNode(componentSet, uxml); 182 | if (node is SliceNode slice) WriteSliceNode(slice, uxml); 183 | if (node is RectangleNode rectangle) WriteDefaultShapeNode(rectangle, uxml); 184 | if (node is LineNode line) WriteDefaultShapeNode(line, uxml); 185 | if (node is EllipseNode ellipse) WriteDefaultShapeNode(ellipse, uxml); 186 | if (node is RegularPolygonNode regularPolygon) WriteDefaultShapeNode(regularPolygon, uxml); 187 | if (node is StarNode star) WriteDefaultShapeNode(star, uxml); 188 | if (node is VectorNode vector) WriteDefaultShapeNode(vector, uxml); 189 | if (node is TextNode text) WriteTextNode(text, uxml); 190 | if (node is ComponentNode component) WriteDefaultFrameNode(component, uxml); 191 | if (node is InstanceNode instance) WriteInstanceNode(instance, uxml); 192 | if (node is BooleanOperationNode booleanOperation) WriteDefaultFrameNode(booleanOperation, uxml); 193 | if (node is SectionNode sectionNode) WriteDefaultFrameNode(sectionNode, uxml); // WriteSectionNode(sectionNode, uxml); 194 | } 195 | #endregion 196 | 197 | #region Support Methods 198 | void WriteStyles(string[] styles, UxmlWriter writer) => styles.ForEach(ussPath => writer.WriteUssStyleReference(CombinePath(GetRelativePath(writer.filePath, ussPath)))); 199 | #endregion 200 | } 201 | } -------------------------------------------------------------------------------- /Editor/Core/Uxml/UxmlBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 501228cf0ce84dfda7160110c165f212 3 | timeCreated: 1727945409 -------------------------------------------------------------------------------- /Editor/Core/Uxml/UxmlWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Xml; 4 | using UnityEngine.UIElements; 5 | 6 | namespace Figma.Core.Uxml 7 | { 8 | using Internals; 9 | using static Internals.Const; 10 | using static Internals.PathExtensions; 11 | 12 | internal sealed class UxmlWriter : IDisposable 13 | { 14 | const string elementsNamespace = "ui"; 15 | 16 | static readonly XmlWriterSettings xmlWriterSettings = new() 17 | { 18 | OmitXmlDeclaration = true, 19 | Indent = true, 20 | IndentChars = indentCharacters, 21 | NewLineOnAttributes = false, 22 | Encoding = Encoding.UTF8 23 | }; 24 | 25 | #region Fields 26 | internal readonly string filePath; 27 | internal readonly XmlWriter xmlWriter; 28 | #endregion 29 | 30 | #region Constructors 31 | public UxmlWriter(string directory, string fileName) 32 | { 33 | filePath = CombinePath(directory, $"{fileName}.{KnownFormats.uxml}"); 34 | xmlWriter = XmlWriter.Create(filePath, xmlWriterSettings); 35 | xmlWriter.WriteStartElement(elementsNamespace, "UXML", uxmlNamespace); 36 | } 37 | #endregion 38 | 39 | #region Methods 40 | public void Dispose() 41 | { 42 | xmlWriter.WriteEndElement(); 43 | xmlWriter?.Dispose(); 44 | } 45 | public void StartElement(BaseNode node, string ussClasses, (ElementType type, string typeFullName) elementTypeInfo) 46 | { 47 | (string prefix, string elementName, string pickingMode) GetElementData(BaseNode node) 48 | { 49 | string prefix = elementsNamespace; 50 | string elementName = nameof(VisualElement); 51 | PickingMode pickingMode = PickingMode.Ignore; 52 | 53 | if (elementTypeInfo.type == ElementType.IElement) 54 | { 55 | prefix = null; 56 | elementName = elementTypeInfo.typeFullName; 57 | pickingMode = PickingMode.Position; 58 | } 59 | else if (elementTypeInfo.type == ElementType.None) 60 | { 61 | if (node is not (DefaultFrameNode or TextNode or ComponentSetNode)) 62 | return (prefix, elementName, pickingMode.ToString()); 63 | 64 | const string inputsPrefix = "Inputs"; 65 | const string buttonsPrefix = "Buttons"; 66 | const string togglesPrefix = "Toggles"; 67 | const string scrollViewsPrefix = "ScrollViews"; 68 | 69 | if (node is TextNode) elementName = node.name.StartsWith(inputsPrefix) ? nameof(TextField) : nameof(Label); 70 | 71 | if (node.name.StartsWith(buttonsPrefix)) elementName = nameof(Button); 72 | else if (node.name.StartsWith(togglesPrefix)) elementName = nameof(Toggle); 73 | else if (node.name.StartsWith(scrollViewsPrefix)) elementName = nameof(ScrollView); 74 | 75 | pickingMode = node.name.StartsWith(buttonsPrefix) || 76 | node.name.StartsWith(togglesPrefix) || 77 | node.name.StartsWith(scrollViewsPrefix) || 78 | (node is TextNode && node.name.StartsWith(inputsPrefix)) 79 | ? PickingMode.Position 80 | : pickingMode; 81 | } 82 | else 83 | { 84 | elementName = elementTypeInfo.type.ToString(); 85 | pickingMode = elementTypeInfo.type is ElementType.VisualElement or 86 | ElementType.BindableElement or 87 | ElementType.Box or 88 | ElementType.TextElement or 89 | ElementType.Label or 90 | ElementType.Image 91 | ? PickingMode.Ignore 92 | : PickingMode.Position; 93 | } 94 | 95 | return (prefix, elementName, pickingMode.ToString()); 96 | } 97 | 98 | (string prefix, string elementName, string pickingMode) = GetElementData(node); 99 | 100 | if (prefix.NotNullOrEmpty()) 101 | xmlWriter.WriteStartElement(prefix, elementName, uxmlNamespace); 102 | else 103 | xmlWriter.WriteStartElement(elementName); 104 | 105 | xmlWriter.WriteAttributeString("name", node.name); 106 | xmlWriter.WriteAttributeString("id", node.id); 107 | 108 | if (ussClasses.NotNullOrEmpty()) 109 | xmlWriter.WriteAttributeString("class", ussClasses); 110 | if (pickingMode != PickingMode.Position.ToString()) 111 | xmlWriter.WriteAttributeString("picking-mode", pickingMode); 112 | } 113 | public void StartElement(string type, params (string name, string value)[] attributes) 114 | { 115 | xmlWriter.WriteStartElement(elementsNamespace, type, uxmlNamespace); 116 | 117 | foreach ((string name, string value) attribute in attributes) 118 | xmlWriter.WriteAttributeString(attribute.name, attribute.value); 119 | } 120 | public void EndElement() => xmlWriter.WriteEndElement(); 121 | 122 | public void WriteUssStyleReference(string path) 123 | { 124 | StartElement(nameof(Style), ("src", path)); 125 | EndElement(); 126 | } 127 | public void WriteTemplate(string templateName, string templatePath) 128 | { 129 | StartElement("Template", ("name", templateName), ("src", templatePath)); 130 | EndElement(); 131 | } 132 | public void WriteInstance(string instanceName, string templateName, string classList = null) 133 | { 134 | StartElement("Instance", ("name", instanceName), ("template", templateName), ("picking-mode", "ignore")); 135 | if (!string.IsNullOrEmpty(classList)) 136 | xmlWriter.WriteAttributeString("class", classList); 137 | EndElement(); 138 | } 139 | #endregion 140 | } 141 | } -------------------------------------------------------------------------------- /Editor/Core/Uxml/UxmlWriter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 32914bba2eaf4c938f1c10a15860d6fa 3 | timeCreated: 1733218152 -------------------------------------------------------------------------------- /Editor/Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 15d902d3df7e2ae40a71e11e3a45e807 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Extensions/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | 6 | namespace Figma 7 | { 8 | using Internals; 9 | 10 | [DebuggerStepThrough] 11 | internal static class Extensions 12 | { 13 | #region Const 14 | static readonly string[] unitsMap = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" }; 15 | static readonly string[] tensMap = { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" }; 16 | #endregion 17 | 18 | #region Methods 19 | internal static int ToBit(this bool value) => value ? 1 : 0; 20 | 21 | internal static IEnumerable IndexRedundantNames(this IReadOnlyList items, Func getName, Action setName, Func postfixConverter) 22 | { 23 | foreach (IGrouping group in items.GroupBy(getName).Where(y => y.Count() > 1)) 24 | { 25 | int i = 0; 26 | foreach (T item in group) 27 | setName(item, postfixConverter(i++)); 28 | } 29 | 30 | return items; 31 | } 32 | internal static string NumberToWords(this int number) 33 | { 34 | switch (number) 35 | { 36 | case 0: 37 | return unitsMap[0]; 38 | 39 | case < 0: 40 | return $"minus-{NumberToWords(Math.Abs(number))}"; 41 | } 42 | 43 | string words = string.Empty; 44 | 45 | if (number / 1000000 > 0) 46 | { 47 | words += $"{NumberToWords(number / 1000000)}-million "; 48 | number %= 1000000; 49 | } 50 | 51 | if (number / 1000 > 0) 52 | { 53 | words += $"{NumberToWords(number / 1000)}-thousand "; 54 | number %= 1000; 55 | } 56 | 57 | if (number / 100 > 0) 58 | { 59 | words += $"{NumberToWords(number / 100)}-hundred "; 60 | number %= 100; 61 | } 62 | 63 | if (number <= 0) 64 | return words; 65 | 66 | if (words != string.Empty) 67 | words += "and-"; 68 | if (number < 20) 69 | words += unitsMap[number]; 70 | else 71 | { 72 | words += tensMap[number / 10]; 73 | if (number % 10 > 0) 74 | words += $"-{unitsMap[number % 10]}"; 75 | } 76 | 77 | return words; 78 | } 79 | internal static RGBA GetAverageColor(this IEnumerable colors) 80 | { 81 | RGBA avgColor = new(); 82 | int count = 0; 83 | 84 | foreach (RGBA color in colors) 85 | { 86 | avgColor.r += color.r; 87 | avgColor.g += color.g; 88 | avgColor.b += color.b; 89 | avgColor.a += color.a; 90 | count++; 91 | } 92 | 93 | if (count == 0) return avgColor; 94 | 95 | avgColor.r /= count; 96 | avgColor.g /= count; 97 | avgColor.b /= count; 98 | avgColor.a /= count; 99 | return avgColor; 100 | } 101 | #endregion 102 | } 103 | } -------------------------------------------------------------------------------- /Editor/Extensions/Extensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7130aa1fa17b4bffa5e38dc5887c8364 3 | timeCreated: 1732199774 -------------------------------------------------------------------------------- /Editor/Extensions/NodeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Security.Cryptography; 7 | using System.Text; 8 | 9 | namespace Figma 10 | { 11 | using Internals; 12 | 13 | [DebuggerStepThrough] 14 | internal static class NodeExtensions 15 | { 16 | internal const int maximumAllowedDepthLimit = 0x10000; // This is a random big number. 17 | internal const string maximumDepthLimitReachedExceptionMessage = "Maximum depth limit is exceeded."; 18 | 19 | #region Methods 20 | internal static IEnumerable Flatten(this IBaseNodeMixin root, Func filter = null) 21 | { 22 | Stack nodes = new(); 23 | nodes.Push(root); 24 | int depth = 0; 25 | 26 | if (root is DocumentNode documentNode) 27 | foreach (CanvasNode canvasNode in documentNode.children) 28 | nodes.Push(canvasNode); 29 | 30 | while (nodes.Count > 0) 31 | { 32 | if (depth++ >= maximumAllowedDepthLimit) 33 | throw new InvalidOperationException(maximumDepthLimitReachedExceptionMessage); 34 | 35 | IBaseNodeMixin node = nodes.Pop(); 36 | 37 | if (filter != null && !filter(node)) 38 | continue; 39 | 40 | yield return node; 41 | 42 | if (node is IChildrenMixin parent) 43 | foreach (SceneNode child in parent.children) 44 | nodes.Push(child); 45 | } 46 | } 47 | internal static bool IsRootNode(this IBaseNodeMixin node) => node is DocumentNode or CanvasNode or ComponentNode || node.parent is CanvasNode or ComponentNode; 48 | internal static bool IsSvgNode(this IBaseNodeMixin node) => node is LineNode or EllipseNode or RegularPolygonNode or StarNode or VectorNode || 49 | (node is BooleanOperationNode && node.Flatten().Any(x => x is not BooleanOperationNode && IsVisible(x) && IsSvgNode(x))); 50 | internal static bool IsVisible(this IBaseNodeMixin node) => (node is not ISceneNodeMixin scene || scene.visible) && (node.parent == null || node.parent.IsVisible()); 51 | internal static bool HasImage(this IBaseNodeMixin node) => node is IGeometryMixin geometry && geometry.fills.Any(x => x is ImagePaint); 52 | 53 | internal static void SetParent(this BaseNode node) 54 | { 55 | switch (node) 56 | { 57 | case DocumentNode document: 58 | foreach (CanvasNode canvas in document.children) 59 | { 60 | canvas.parent = node; 61 | SetParent(canvas); 62 | } 63 | 64 | break; 65 | 66 | case IChildrenMixin { children: not null } children: 67 | foreach (SceneNode child in children.children) 68 | { 69 | child.parent = node; 70 | SetParent(child); 71 | } 72 | 73 | break; 74 | } 75 | } 76 | internal static string GetHash(this GradientPaint gradient) 77 | { 78 | using SHA1CryptoServiceProvider sha1 = new(); 79 | using MemoryStream stream = new(); 80 | using BinaryWriter writer = new(stream); 81 | 82 | writer.Write((int)gradient.type); 83 | foreach (ColorStop stop in gradient.gradientStops) 84 | { 85 | writer.Write(stop.position); 86 | writer.Write(stop.color.r); 87 | writer.Write(stop.color.g); 88 | writer.Write(stop.color.b); 89 | writer.Write(stop.color.a); 90 | } 91 | 92 | foreach (Vector position in gradient.gradientHandlePositions) 93 | { 94 | writer.Write(position.x); 95 | writer.Write(position.y); 96 | } 97 | 98 | byte[] bytes = stream.ToArray(); 99 | byte[] hashBytes = sha1.ComputeHash(bytes); 100 | 101 | StringBuilder hashBuilder = new(); 102 | foreach (byte @byte in hashBytes) 103 | hashBuilder.Append(@byte.ToString("x2")); 104 | 105 | return hashBuilder.ToString(); 106 | } 107 | internal static string GetFullPath(this IBaseNodeMixin node) 108 | { 109 | List names = new(8) { node.name }; 110 | 111 | int depth = 0; 112 | while (node is not null) 113 | { 114 | if (depth++ >= maximumAllowedDepthLimit) 115 | throw new InvalidOperationException(maximumDepthLimitReachedExceptionMessage); 116 | 117 | node = node.parent; 118 | 119 | if (node == null) 120 | break; 121 | 122 | names.Add(node.name); 123 | } 124 | 125 | names.Reverse(); 126 | return PathExtensions.CombinePath(names.ToArray()); 127 | } 128 | #endregion 129 | } 130 | } -------------------------------------------------------------------------------- /Editor/Extensions/NodeExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bc624b728724707428b12ccb11dd0658 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Extensions/UssStyleExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Figma 5 | { 6 | using Core.Uss; 7 | using Internals; 8 | 9 | static class UssStyleExtension 10 | { 11 | #region Methods 12 | internal static bool HasBorder(this IGeometryMixin geometry) => geometry.strokes.Any(x => x.visible) && geometry.strokeWeight > UssStyle.tolerance; 13 | internal static LayoutDouble4 GetBorderWidths(this IGeometryMixin geometry) 14 | { 15 | if (geometry is null or TextNode || !HasBorder(geometry)) return new LayoutDouble4(); 16 | 17 | LayoutDouble4 borders = geometry.individualStrokeWeights != null 18 | ? new LayoutDouble4(geometry.individualStrokeWeights.top, geometry.individualStrokeWeights.right, geometry.individualStrokeWeights.bottom, geometry.individualStrokeWeights.left) 19 | : new LayoutDouble4(geometry.strokeWeight); 20 | 21 | return borders; 22 | } 23 | internal static LayoutDouble4 GetOutsideBorderWidths(this IGeometryMixin geometry) => geometry.GetBorderWidths() * geometry.GetOutsideFraction(); 24 | internal static LayoutDouble4 GetInsideBorderWidths(this IGeometryMixin geometry) => geometry.GetBorderWidths() * (1 - geometry.GetOutsideFraction()); 25 | internal static Rect GetContentBox(this ILayoutMixin layout) 26 | { 27 | LayoutDouble4 border = GetInsideBorderWidths(layout as IGeometryMixin); 28 | double x = layout.absoluteBoundingBox.x + border.left; 29 | double y = layout.absoluteBoundingBox.y + border.top; 30 | double width = layout.absoluteBoundingBox.width - border.left - border.right; 31 | double height = layout.absoluteBoundingBox.height - border.top - border.bottom; 32 | return new Rect(x, y, width, height); 33 | } 34 | internal static Rect GetBorderBox(this ILayoutMixin layout) 35 | { 36 | LayoutDouble4 border = GetOutsideBorderWidths(layout as IGeometryMixin); 37 | double x = layout.absoluteBoundingBox.x - border.left; 38 | double y = layout.absoluteBoundingBox.y - border.top; 39 | double width = layout.absoluteBoundingBox.width + border.left + border.right; 40 | double height = layout.absoluteBoundingBox.height + border.top + border.bottom; 41 | return new Rect(x, y, width, height); 42 | } 43 | internal static LayoutDouble4 GetCorrectedPadding(this IDefaultFrameMixin frame) => new LayoutDouble4(frame.paddingTop, frame.paddingRight, frame.paddingBottom, frame.paddingLeft) - (frame.strokesIncludedInLayout ? new LayoutDouble4() : GetInsideBorderWidths(frame)); 44 | internal static RGBA BlendWith(this RGBA foreground, RGBA background) 45 | { 46 | double blend = background.a * (1.0 - foreground.a); 47 | RGBA color = new(); 48 | color.a = foreground.a + blend; 49 | 50 | const double alphaTolerance = 0.01; 51 | if (color.a < alphaTolerance) return new RGBA(); 52 | 53 | color.r = (foreground.r * foreground.a + background.r * blend) / color.a; 54 | color.g = (foreground.g * foreground.a + background.g * blend) / color.a; 55 | color.b = (foreground.b * foreground.a + background.b * blend) / color.a; 56 | 57 | return color; 58 | } 59 | #endregion 60 | 61 | #region Support Methods 62 | static double GetOutsideFraction(this IGeometryMixin geometry) => geometry.strokeAlign switch 63 | { 64 | StrokeAlign.OUTSIDE => 1.0, 65 | StrokeAlign.CENTER => 0.5, 66 | StrokeAlign.INSIDE => 0.0, 67 | _ => throw new NotSupportedException() 68 | }; 69 | #endregion 70 | } 71 | } -------------------------------------------------------------------------------- /Editor/Extensions/UssStyleExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cd5b7b5e37724678a84ec7eb169a0c3d 3 | timeCreated: 1744032663 -------------------------------------------------------------------------------- /Editor/Figma.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Figma.Editor", 3 | "rootNamespace": "Figma.Editor", 4 | "references": [ 5 | "Unity.VectorGraphics.Editor", 6 | "AsyncAwaitUtil", 7 | "CommonUtils", 8 | "Figma" 9 | ], 10 | "includePlatforms": [ 11 | "Editor" 12 | ] 13 | } -------------------------------------------------------------------------------- /Editor/Figma.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5eb4f7c69a0b05842a5bd445fe8970e8 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/FigmaDownloader.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 51ab4c8a6a174660b9a8bff7530d8631 3 | timeCreated: 1695908086 -------------------------------------------------------------------------------- /Editor/FigmaWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | // ReSharper disable UnusedMember.Local 7 | // ReSharper disable UnusedParameter.Local 8 | 9 | #pragma warning disable S1144 // Unused private types or members should be removed 10 | 11 | namespace Figma 12 | { 13 | using Core; 14 | using Core.Assets; 15 | using Core.Uss; 16 | using Core.Uxml; 17 | using Internals; 18 | using static Internals.Const; 19 | using static Internals.PathExtensions; 20 | 21 | internal sealed class FigmaWriter 22 | { 23 | #region Fields 24 | readonly string directory; 25 | readonly string fileName; 26 | readonly string ussPath; 27 | readonly Data data; 28 | readonly NodeMetadata nodeMetadata; 29 | readonly AssetsInfo assetsInfo; 30 | readonly StylesPreprocessor stylesPreprocessor; 31 | #endregion 32 | 33 | #region Properties 34 | DocumentNode Document => data.document; 35 | #endregion 36 | 37 | #region Constructors 38 | internal FigmaWriter(string directory, string fileName, Data data, StylesPreprocessor stylesPreprocessor, NodeMetadata nodeMetadata, AssetsInfo assetsInfo) 39 | { 40 | this.directory = directory; 41 | this.fileName = fileName; 42 | this.data = data; 43 | this.nodeMetadata = nodeMetadata; 44 | this.assetsInfo = assetsInfo; 45 | this.stylesPreprocessor = stylesPreprocessor; 46 | 47 | ussPath = CombinePath(directory, $"{fileName}.{KnownFormats.uss}"); 48 | } 49 | #endregion 50 | 51 | #region Methods 52 | internal async Task WriteAsync(bool overrideGlobal = false) 53 | { 54 | RootNodes rootNodes = new(data, nodeMetadata); 55 | // We do need this, since the Data.componentSets do not contain updated names. 56 | Dictionary componentSets = rootNodes.ComponentSets 57 | .OrderBy(x => x.id) // Ordering to avoid index confusion, since order in the Collection could vary from one request to another. 58 | .ToArray() 59 | .IndexRedundantNames(x => x.name, 60 | (componentSet, postfix) => componentSet.name += postfix, 61 | index => index == 0 ? string.Empty : "-" + index) 62 | .ToDictionary(x => x.id); 63 | 64 | KeyValuePair[] nodeStyleFiltered = stylesPreprocessor.NodeStyleMap.Where(x => x.Key.IsVisible() && (nodeMetadata.EnabledInHierarchy(x.Key) || x.Key is ComponentSetNode)).ToArray(); 65 | UssStyle[] nodeStyleStatelessFiltered = nodeStyleFiltered.Select(x => x.Value).ToArray(); 66 | UssStyle[] globalStaticStyles = stylesPreprocessor.Styles.Select(x => x.style).Where(x => nodeStyleStatelessFiltered.Any(y => y.DoesInherit(x))).ToArray(); 67 | 68 | if (overrideGlobal) 69 | { 70 | await using UssWriter globalUssWriter = new(directory, ussPath); 71 | globalUssWriter.Write(UssStyle.overrideClass); 72 | globalUssWriter.Write(UssStyle.viewportClass); 73 | globalUssWriter.Write(globalStaticStyles.ToArray().IndexRedundantNames(x => x.Name, (style, postfix) => style.Name += postfix, index => "-" + (index + 1).NumberToWords())); 74 | } 75 | 76 | // Writing UXML files 77 | UxmlBuilder uxmlBuilder = new(data, nodeMetadata, ussPath, stylesPreprocessor.GetClassList); 78 | Dictionary> framesPaths = new(rootNodes.Frames.Count); 79 | 80 | foreach (CanvasNode canvasNode in rootNodes.Canvases) 81 | framesPaths.Add(canvasNode.name, new List()); 82 | 83 | List tasks = new(rootNodes.Frames.Count + rootNodes.ComponentSets.Count + rootNodes.Elements.Count); 84 | tasks.AddRange(rootNodes.Frames.Select(x => Task.Run(() => WriteFrame(uxmlBuilder, framesPaths, componentSets, x)))); 85 | tasks.AddRange(rootNodes.ComponentSets.Select(x => Task.Run(() => WriteComponentSet(uxmlBuilder, x)))); 86 | tasks.AddRange(rootNodes.Elements.Select(x => Task.Run(() => WriteTemplate(uxmlBuilder, x)))); 87 | 88 | await Task.WhenAll(tasks); 89 | 90 | // Creating main UXML document 91 | if (overrideGlobal) 92 | uxmlBuilder.CreateDocument(directory, fileName, data.document, framesPaths); 93 | } 94 | #endregion 95 | 96 | #region Support Methods 97 | void WriteFrame(UxmlBuilder uxmlBuilder, Dictionary> framesPaths, Dictionary componentSets, FrameNode frameNode) 98 | { 99 | Dictionary templates = new(); 100 | 101 | void FindTemplates(BaseNode root) 102 | { 103 | Stack nodes = new(); 104 | nodes.Push(root); 105 | int i = 0; 106 | 107 | while (nodes.Count > 0) 108 | { 109 | if (i++ >= NodeExtensions.maximumAllowedDepthLimit) 110 | throw new System.InvalidOperationException(NodeExtensions.maximumDepthLimitReachedExceptionMessage); 111 | 112 | BaseNode node = nodes.Pop(); 113 | 114 | if (!node.IsVisible() || !nodeMetadata.EnabledInHierarchy(node)) 115 | continue; 116 | 117 | if (node is InstanceNode instanceNode) 118 | { 119 | Component component = data.components[instanceNode.componentId]; 120 | 121 | if (component == null || component.remote || string.IsNullOrEmpty(component.componentSetId)) 122 | continue; 123 | 124 | Component componentSet = data.componentSets[component.componentSetId]; 125 | 126 | if (componentSet == null || componentSet.remote) 127 | continue; 128 | 129 | string template = componentSets[component.componentSetId].name; 130 | templates[template] = CombinePath(directory, componentsDirectoryName, $"{template}.{KnownFormats.uxml}"); 131 | } 132 | else if (nodeMetadata.GetTemplate(node) is (_, { } template) && template.NotNullOrEmpty()) 133 | { 134 | templates[template] = CombinePath(directory, elementsDirectoryName, $"{template}.{KnownFormats.uxml}"); 135 | } 136 | 137 | if (node is DefaultFrameNode frameNode) 138 | foreach (SceneNode child in frameNode.children) 139 | nodes.Push(child); 140 | } 141 | } 142 | 143 | string rootDirectory = CombinePath(directory, framesDirectoryName, frameNode.parent.name); 144 | 145 | if (!Directory.Exists(rootDirectory)) 146 | Directory.CreateDirectory(rootDirectory); 147 | 148 | using UssWriter ussWriter = new(directory, CombinePath(rootDirectory, $"{frameNode.name}.{KnownFormats.uss}")); 149 | ussWriter.Write(stylesPreprocessor.GetStyles(frameNode).IndexRedundantNames(x => x.Name, (style, postfix) => style.Name += postfix, index => "-" + (index + 1).NumberToWords())); 150 | 151 | FindTemplates(frameNode); 152 | 153 | string uxmlPath = uxmlBuilder.CreateFrame(rootDirectory, new[] { ussPath, ussWriter.Path }, templates, frameNode); 154 | framesPaths[frameNode.parent.name].As>().Add(uxmlPath); 155 | 156 | assetsInfo.AddModifiedFiles(uxmlPath, ussWriter.Path); 157 | templates.Clear(); 158 | } 159 | void WriteComponentSet(UxmlBuilder uxmlBuilder, ComponentSetNode componentSet) 160 | { 161 | using UssWriter ussWriter = new(directory, CombinePath(directory, componentsDirectoryName, $"{componentSet.name}.{KnownFormats.uss}")); 162 | ussWriter.Write(stylesPreprocessor.GetStyles(componentSet).IndexRedundantNames(x => x.Name, (style, postfix) => style.Name += postfix, index => "-" + (index + 1).NumberToWords())); 163 | 164 | string uxmlPath = uxmlBuilder.CreateComponentSet(CombinePath(directory, componentsDirectoryName), new[] { ussPath, ussWriter.Path }, componentSet); 165 | assetsInfo.AddModifiedFiles(uxmlPath, ussWriter.Path); 166 | } 167 | void WriteTemplate(UxmlBuilder uxmlBuilder, (DefaultShapeNode element, string template) node) 168 | { 169 | (bool isHash, string hashedTemplates) = nodeMetadata.GetTemplate(node.element); 170 | 171 | using UssWriter ussWriter = new(directory, CombinePath(directory, elementsDirectoryName, $"{(isHash ? hashedTemplates : node.template)}.{KnownFormats.uss}")); 172 | ussWriter.Write(stylesPreprocessor.GetStyles(node.element).IndexRedundantNames(x => x.Name, (style, postfix) => style.Name += postfix, index => "-" + (index + 1).NumberToWords())); 173 | 174 | string uxmlPath = uxmlBuilder.CreateElement(CombinePath(directory, elementsDirectoryName), new[] { ussPath, ussWriter.Path }, node.element, node.template); 175 | assetsInfo.AddModifiedFiles(uxmlPath, ussWriter.Path); 176 | } 177 | #endregion 178 | } 179 | } -------------------------------------------------------------------------------- /Editor/FigmaWriter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cbfef092900681d478f7beff22ae8f28 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Inspector.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ee4e1dc47f503814581a191eb139e1b8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Inspector/AuthTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Figma.Inspectors 5 | { 6 | using Internals; 7 | 8 | internal class AuthTest : Api 9 | { 10 | #region Properties 11 | public Me me { get; private set; } 12 | public bool IsAuthenticated => me != null && string.IsNullOrEmpty(me.err) && me.email.Contains("@"); 13 | #endregion 14 | 15 | #region Constructors 16 | internal AuthTest(string personalAccessToken = null) : base(personalAccessToken, null) { } 17 | #endregion 18 | 19 | #region Methods 20 | internal async Task AuthAsync() => me = await GetAsync(nameof(me), CancellationToken.None); 21 | #endregion 22 | } 23 | } -------------------------------------------------------------------------------- /Editor/Inspector/AuthTest.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b2f4d6368ca84ba2860d8499592a9019 3 | timeCreated: 1696830428 -------------------------------------------------------------------------------- /Editor/Inspector/FigmaInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 79908b73109533b4d87fced3ad4caee7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Inspector/Styles.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | 4 | namespace Figma.Inspectors 5 | { 6 | public static class Styles 7 | { 8 | internal static readonly string SuccessColor = EditorGUIUtility.isProSkin ? "#00ff00" : "#00aa00"; 9 | 10 | static readonly string prefix = EditorGUIUtility.isProSkin ? "d_" : string.Empty; 11 | 12 | internal static readonly Texture DirectoryIcon = EditorGUIUtility.IconContent($"{prefix}Project").image; 13 | internal static readonly Texture LoggedInIcon = EditorGUIUtility.IconContent("TestPassed").image; 14 | internal static readonly Texture LogOutIcon = EditorGUIUtility.IconContent($"{prefix}Import").image; 15 | internal static readonly Texture DocumentsOnlyIcon = EditorGUIUtility.IconContent($"{prefix}Refresh@2x").image; 16 | internal static readonly Texture DocumentWithImagesIcon = EditorGUIUtility.IconContent($"{prefix}RawImage Icon").image; 17 | } 18 | } -------------------------------------------------------------------------------- /Editor/Inspector/Styles.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 54fcfd1c480a4ad290dcae61139be13f 3 | timeCreated: 1739275722 -------------------------------------------------------------------------------- /Editor/Interface.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9d9c7aa8bf559004a898cba5d982b661 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Interface/Const.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace Figma.Internals 4 | { 5 | public static class Const 6 | { 7 | // Http API target 8 | public const string api = "https://api.figma.com/v1"; 9 | 10 | // Directories 11 | public const string fontsDirectoryName = "Fonts"; 12 | public const string framesDirectoryName = "Frames"; 13 | public const string imagesDirectoryName = nameof(Images); 14 | public const string elementsDirectoryName = "Elements"; 15 | public const string componentsDirectoryName = "Components"; 16 | 17 | // Uxml 18 | public const string uxmlNamespace = "UnityEngine.UIElements"; 19 | public const string indentCharacters = " "; 20 | 21 | // Fallback written data 22 | /// 23 | /// Magenta colored image with resolution 2x2. 24 | /// 25 | public static readonly byte[] InvalidPng = 26 | { 27 | 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature 28 | // IHDR chunk 29 | 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 30 | 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 31 | 0x08, 0x06, 0x00, 0x00, 0x00, 0xF4, 0x78, 0x5A, 0xEE, 32 | // IDAT chunk (compressed image data) 33 | 0x00, 0x00, 0x00, 0x11, 0x49, 0x44, 0x41, 0x54, 34 | 0x78, 0x9C, 0x63, 0xF8, 0xCF, 0xC0, 0xC0, 0xC0, 35 | 0xF0, 0x0F, 0x04, 0x00, 0x04, 0x00, 0x01, 0xF3, 36 | 0x0D, 0x0E, 0x43, 37 | // IEND chunk 38 | 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 39 | 0xAE, 0x42, 0x60, 0x82 40 | }; 41 | /// 42 | /// Warning sign in SVG. 43 | /// 44 | public const string InvalidSvg = @" 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 58 | 60 | 61 | "; 62 | 63 | public static readonly CultureInfo Culture = CultureInfo.GetCultureInfo("en-US"); 64 | 65 | public const int initialCollectionCapacity = 128; 66 | } 67 | 68 | public static class KnownFormats 69 | { 70 | public const string png = nameof(png); 71 | public const string svg = nameof(svg); 72 | public const string ttf = nameof(ttf); 73 | public const string otf = nameof(otf); 74 | public const string asset = nameof(asset); 75 | public const string uxml = nameof(uxml); 76 | public const string uss = nameof(uss); 77 | public const string json = nameof(json); 78 | public const string meta = nameof(meta); 79 | } 80 | } -------------------------------------------------------------------------------- /Editor/Interface/Const.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e79ce7b985a449c58c07592648c530b7 3 | timeCreated: 1728029275 -------------------------------------------------------------------------------- /Editor/Interface/Enums.cs: -------------------------------------------------------------------------------- 1 | namespace Figma 2 | { 3 | enum Unit 4 | { 5 | Default, 6 | None, 7 | Initial, 8 | Auto, 9 | Pixel, 10 | Degrees, 11 | Percent 12 | } 13 | 14 | enum Align 15 | { 16 | Auto, 17 | FlexStart, 18 | FlexEnd, 19 | Center, 20 | Stretch 21 | } 22 | 23 | enum FlexDirection 24 | { 25 | Row, 26 | RowReverse, 27 | Column, 28 | ColumnReverse 29 | } 30 | 31 | enum FlexWrap 32 | { 33 | Nowrap, 34 | Wrap, 35 | WrapReverse 36 | } 37 | 38 | enum JustifyContent 39 | { 40 | FlexStart, 41 | FlexEnd, 42 | Center, 43 | SpaceBetween, 44 | SpaceAround 45 | } 46 | 47 | enum Position 48 | { 49 | Absolute, 50 | Relative 51 | } 52 | 53 | enum Visibility 54 | { 55 | Visible, 56 | Hidden 57 | } 58 | 59 | enum OverflowClip 60 | { 61 | PaddingBox, 62 | ContentBox 63 | } 64 | 65 | enum Display 66 | { 67 | Flex, 68 | None 69 | } 70 | 71 | enum FontStyle 72 | { 73 | Normal, 74 | Italic, 75 | Bold, 76 | BoldAndItalic 77 | } 78 | 79 | enum TextAlign 80 | { 81 | UpperLeft, 82 | MiddleLeft, 83 | LowerLeft, 84 | UpperCenter, 85 | MiddleCenter, 86 | LowerCenter, 87 | UpperRight, 88 | MiddleRight, 89 | LowerRight 90 | } 91 | 92 | enum EasingFunction 93 | { 94 | EaseIn, 95 | EaseOut, 96 | EaseInAndOut, 97 | Linear, 98 | Slow, 99 | CustomSpring 100 | } 101 | 102 | enum Wrap 103 | { 104 | Normal, 105 | Nowrap, 106 | } 107 | 108 | public enum ElementType 109 | { 110 | None, 111 | 112 | //Base elements 113 | VisualElement, 114 | BindableElement, 115 | 116 | //Utilities 117 | Box, 118 | TextElement, 119 | Label, 120 | Image, 121 | IMGUIContainer, 122 | Foldout, 123 | 124 | //Templates 125 | Template, 126 | Instance, 127 | TemplateContainer, 128 | 129 | //Controls 130 | Button, 131 | RepeatButton, 132 | Toggle, 133 | Scroller, 134 | Slider, 135 | SliderInt, 136 | MinMaxSlider, 137 | EnumField, 138 | MaskField, 139 | LayerField, 140 | LayerMaskField, 141 | TagField, 142 | ProgressBar, 143 | 144 | //Text input 145 | TextField, 146 | IntegerField, 147 | LongField, 148 | FloatField, 149 | DoubleField, 150 | Vector2Field, 151 | Vector2IntField, 152 | Vector3Field, 153 | Vector3IntField, 154 | Vector4Field, 155 | RectField, 156 | RectIntField, 157 | BoundsField, 158 | BoundsIntField, 159 | 160 | //Complex widgets 161 | PropertyField, 162 | PropertyControlInt, 163 | PropertyControlLong, 164 | PropertyControlFloat, 165 | PropertyControlDouble, 166 | PropertyControlString, 167 | ColorField, 168 | CurveField, 169 | GradientField, 170 | ObjectField, 171 | 172 | //Toolbar 173 | Toolbar, 174 | ToolbarButton, 175 | ToolbarToggle, 176 | ToolbarMenu, 177 | ToolbarSearchField, 178 | ToolbarPopupSearchField, 179 | ToolbarSpacer, 180 | 181 | //Views and windows 182 | ListView, 183 | ScrollView, 184 | TreeView, 185 | PopupWindow, 186 | 187 | IElement 188 | } 189 | 190 | enum TimeUnit 191 | { 192 | Default, 193 | Millisecond, 194 | Second 195 | } 196 | 197 | enum PseudoClass 198 | { 199 | None = 0, 200 | Hover, 201 | Active, 202 | Inactive, 203 | Focus, 204 | Selected, 205 | Disabled, 206 | Enabled, 207 | Checked, 208 | Root 209 | } 210 | 211 | enum FontWeight 212 | { 213 | Thin = 100, 214 | ExtraLight = 200, 215 | Light = 300, 216 | Regular = 400, 217 | Medium = 500, 218 | SemiBold = 600, 219 | Bold = 700, 220 | ExtraBold = 800, 221 | Black = 900 222 | } 223 | } -------------------------------------------------------------------------------- /Editor/Interface/Enums.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 772df3471b2405947abf34d0b8abcf8f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Interface/Figma.Enums.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable InconsistentNaming 2 | 3 | namespace Figma.Internals 4 | { 5 | public enum EffectType { INNER_SHADOW, DROP_SHADOW, LAYER_BLUR, BACKGROUND_BLUR } 6 | 7 | public enum BlendMode { PASS_THROUGH, NORMAL, DARKEN, MULTIPLY, LINEAR_BURN, COLOR_BURN, LIGHTEN, SCREEN, LINEAR_DODGE, COLOR_DODGE, OVERLAY, SOFT_LIGHT, HARD_LIGHT, DIFFERENCE, EXCLUSION, HUE, SATURATION, COLOR, LUMINOSITY } 8 | 9 | public enum ConstraintVertical { TOP, BOTTOM, CENTER, TOP_BOTTOM, SCALE } 10 | 11 | public enum ConstraintHorizontal { LEFT, RIGHT, CENTER, LEFT_RIGHT, SCALE } 12 | 13 | public enum PaintType { SOLID, GRADIENT_LINEAR, GRADIENT_RADIAL, GRADIENT_ANGULAR, GRADIENT_DIAMOND, IMAGE, EMOJI } 14 | 15 | public enum Pattern { COLUMNS, ROWS, GRID } 16 | 17 | public enum Alignment { MIN, MAX, STRETCH, CENTER } 18 | 19 | public enum ScaleMode { FILL, FIT, TILE, STRETCH } 20 | 21 | public enum ExportSettingsConstraintsType { SCALE, WIDTH, HEIGHT } 22 | 23 | public enum Format { JPG, PNG, SVG, PDF } 24 | 25 | public enum ActionType { BACK, CLOSE, URL, NODE } 26 | 27 | public enum Navigation { NAVIGATE, SWAP, OVERLAY, CHANGE_TO } 28 | 29 | public enum TransitionType { DISSOLVE, SMART_ANIMATE, MOVE_IN, MOVE_OUT, PUSH, SLIDE_IN, SLIDE_OUT } 30 | 31 | public enum TransitionDirection { LEFT, RIGHT, TOP, BOTTOM } 32 | 33 | public enum TriggerType { ON_CLICK, ON_HOVER, ON_PRESS, DRAG, AFTER_TIMEOUT, MOUSE_ENTER, MOUSE_LEAVE, MOUSE_UP, MOUSE_DOWN, ON_KEY_DOWN, ON_KEY_UP } 34 | 35 | public enum TriggerDevice { KEYBOARD, XBOX_ONE, PS4, SWITCH_PRO, UNKNOWN_CONTROLLER } 36 | 37 | public enum EasingType { EASE_IN, EASE_OUT, EASE_IN_AND_OUT, LINEAR, SLOW, CUSTOM_SPRING } 38 | 39 | public enum LayoutAlign { CENTER, MIN, MAX, STRETCH, INHERIT } 40 | 41 | public enum StrokeCap { NONE, ROUND, SQUARE, ARROW_LINES, ARROW_EQUILATERAL, LINE_ARROW } 42 | 43 | public enum StrokeJoin { MITER, BEVEL, ROUND } 44 | 45 | public enum StrokeAlign { INSIDE, OUTSIDE, CENTER } 46 | 47 | public enum LayoutMode { NONE, HORIZONTAL, VERTICAL } 48 | 49 | public enum PrimaryAxisSizingMode { AUTO, FIXED } 50 | 51 | public enum CounterAxisSizingMode { AUTO, FIXED } 52 | 53 | public enum PrimaryAxisAlignItems { MIN, CENTER, MAX, SPACE_BETWEEN } 54 | 55 | public enum CounterAxisAlignItems { MIN, CENTER, MAX, BASELINE } 56 | 57 | public enum OverflowDirection { NONE, HORIZONTAL_SCROLLING, VERTICAL_SCROLLING, HORIZONTAL_AND_VERTICAL_SCROLLING } 58 | 59 | public enum TextCase { ORIGINAL, UPPER, LOWER, TITLE } 60 | 61 | public enum TextDecoration { NONE, UNDERLINE, STRIKETHROUGH } 62 | 63 | public enum TextAlignHorizontal { LEFT, CENTER, RIGHT, JUSTIFIED } 64 | 65 | public enum TextAlignVertical { TOP, CENTER, BOTTOM } 66 | 67 | public enum TextAutoResize { NONE, WIDTH_AND_HEIGHT, HEIGHT } 68 | 69 | public enum BooleanOperation { UNION, INTERSECT, SUBTRACT, EXCLUDE } 70 | 71 | public enum LayoutPositioning { AUTO, ABSOLUTE } 72 | 73 | public enum StyleType { FILL, TEXT, EFFECT, GRID, NONE } 74 | 75 | public enum NodeType { DOCUMENT, CANVAS, SLICE, FRAME, GROUP, COMPONENT_SET, COMPONENT, INSTANCE, BOOLEAN_OPERATION, VECTOR, STAR, LINE, ELLIPSE, REGULAR_POLYGON, RECTANGLE, TEXT, SECTION } 76 | 77 | public enum ComponentPropertyType { BOOLEAN, TEXT, INSTANCE_SWAP, VARIANT } 78 | 79 | public enum LayoutWrap { NO_WRAP, WRAP } 80 | 81 | public enum MaskType { ALPHA, VECTOR, LUMINANCE } 82 | 83 | public enum LayoutSizing { FIXED, HUG, FILL } 84 | 85 | public enum CounterAxisAlignContent { AUTO, SPACE_BETWEEN } 86 | 87 | public enum TextTruncation { DISABLED, ENDING } 88 | 89 | public enum LineType { NONE, ORDERED, UNORDERED } 90 | 91 | public enum TextWeight { BOLD, NORMAL } 92 | 93 | public enum TextItalic { ITALIC, NORMAL } 94 | } -------------------------------------------------------------------------------- /Editor/Interface/Figma.Enums.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 04cc6de496b9b534d9dbdcfae4f2353e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Interface/Figma.Types.Interface.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | // ReSharper disable InconsistentNaming 5 | // ReSharper disable BuiltInTypeReferenceStyle 6 | // ReSharper disable UnusedMember.Global 7 | // ReSharper disable CollectionNeverUpdated.Global 8 | // ReSharper disable UnassignedField.Global 9 | // ReSharper disable FieldCanBeMadeReadOnly.Global 10 | 11 | #pragma warning disable S101, S4004 12 | 13 | namespace Figma.Internals 14 | { 15 | public interface IBaseNodeMixin 16 | { 17 | NodeType type { get; set; } 18 | string id { get; set; } 19 | [JsonIgnore] BaseNode parent { get; set; } 20 | string name { get; set; } 21 | } 22 | 23 | public interface ISceneNodeMixin 24 | { 25 | bool visible { get; set; } 26 | } 27 | 28 | public interface IChildrenMixin 29 | { 30 | SceneNode[] children { get; set; } 31 | } 32 | 33 | public interface ILayoutMixin 34 | { 35 | Constraints constraints { get; set; } 36 | LayoutAlign layoutAlign { get; set; } 37 | double layoutGrow { get; set; } 38 | LayoutSizing layoutSizingHorizontal { get; set; } 39 | LayoutSizing layoutSizingVertical { get; set; } 40 | Rect absoluteBoundingBox { get; set; } 41 | double rotation { get; set; } 42 | double[][] relativeTransform { get; set; } 43 | Vector? size { get; set; } 44 | double? minWidth { get; set; } 45 | double? minHeight { get; set; } 46 | double? maxWidth { get; set; } 47 | double? maxHeight { get; set; } 48 | } 49 | 50 | public interface IBlendMixin 51 | { 52 | double opacity { get; set; } 53 | BlendMode blendMode { get; set; } 54 | bool isMask { get; set; } 55 | Effect[] effects { get; set; } 56 | Dictionary styles { get; set; } 57 | bool preserveRatio { get; set; } 58 | } 59 | 60 | public interface IGeometryMixin 61 | { 62 | Paint[] fills { get; set; } 63 | object[] fillGeometry { get; set; } 64 | Paint[] strokes { get; set; } 65 | double strokeWeight { get; set; } 66 | StrokeAlign strokeAlign { get; set; } 67 | StrokeCap strokeCap { get; set; } 68 | StrokeJoin strokeJoin { get; set; } 69 | object[] strokeGeometry { get; set; } 70 | IndividualStrokeWeights individualStrokeWeights { get; set; } 71 | } 72 | 73 | public interface ICornerMixin 74 | { 75 | double? cornerRadius { get; set; } 76 | } 77 | 78 | public interface IRectangleCornerMixin 79 | { 80 | double[] rectangleCornerRadii { get; set; } 81 | } 82 | 83 | public interface IExportMixin 84 | { 85 | ExportSettings[] exportSettings { get; set; } 86 | } 87 | 88 | public interface IReactionMixin 89 | { 90 | Reaction[] reactions { get; set; } 91 | } 92 | 93 | public interface ITransitionMixin 94 | { 95 | string transitionNodeID { get; set; } 96 | double? transitionDuration { get; set; } 97 | EasingType? transitionEasing { get; set; } 98 | } 99 | 100 | public interface IDefaultShapeMixin : IBaseNodeMixin, ISceneNodeMixin, ILayoutMixin, IBlendMixin, IGeometryMixin, IReactionMixin, IExportMixin { } 101 | 102 | public interface IDefaultFrameMixin : IDefaultShapeMixin, ICornerMixin, IRectangleCornerMixin, IChildrenMixin 103 | { 104 | LayoutMode layoutMode { get; set; } 105 | LayoutPositioning layoutPositioning { get; set; } 106 | PrimaryAxisSizingMode primaryAxisSizingMode { get; set; } 107 | PrimaryAxisAlignItems primaryAxisAlignItems { get; set; } 108 | CounterAxisSizingMode counterAxisSizingMode { get; set; } 109 | CounterAxisAlignItems counterAxisAlignItems { get; set; } 110 | CounterAxisAlignContent counterAxisAlignContent { get; set; } 111 | double paddingLeft { get; set; } 112 | double paddingTop { get; set; } 113 | double paddingRight { get; set; } 114 | double paddingBottom { get; set; } 115 | double itemSpacing { get; set; } 116 | LayoutGrid[] layoutGrids { get; set; } 117 | bool clipsContent { get; set; } 118 | OverflowDirection overflowDirection { get; set; } 119 | LayoutWrap layoutWrap { get; set; } 120 | bool itemReverseZIndex { get; set; } 121 | bool strokesIncludedInLayout { get; set; } 122 | } 123 | } -------------------------------------------------------------------------------- /Editor/Interface/Figma.Types.Interface.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d433f8c0bc1b474487e41e90ae1ffbd3 3 | timeCreated: 1728645091 -------------------------------------------------------------------------------- /Editor/Interface/Figma.Types.Structs.cs: -------------------------------------------------------------------------------- 1 | namespace Figma.Internals 2 | { 3 | public struct Vector 4 | { 5 | public double x; 6 | public double y; 7 | } 8 | 9 | public struct Rect 10 | { 11 | public double x; 12 | public double y; 13 | public double width; 14 | public double height; 15 | 16 | public double left => x; 17 | public double right => x + width; 18 | public double top => y; 19 | public double bottom => y + height; 20 | public double centerLeft => x - width / 2; 21 | public double centerRight => x + width / 2; 22 | public double centerTop => y - height / 2; 23 | public double centerBottom => y + height / 2; 24 | public double halfWidth => width / 2; 25 | public double halfHeight => height / 2; 26 | 27 | public Rect(double x, double y, double width, double height) 28 | { 29 | this.x = x; 30 | this.y = y; 31 | this.width = width; 32 | this.height = height; 33 | } 34 | 35 | public static Rect operator +(Rect a, Rect b) => new(a.x + b.x, a.y + b.y, a.width + b.width, a.height + b.height); 36 | public static Rect operator -(Rect a, Rect b) => new(a.x - b.x, a.y - b.y, a.width - b.width, a.height - b.height); 37 | } 38 | 39 | public struct RGBA 40 | { 41 | public double r; 42 | public double g; 43 | public double b; 44 | public double a; 45 | 46 | public static explicit operator UnityEngine.Color(RGBA color) => new((float) color.r, (float) color.g, (float) color.b, (float) color.a); 47 | } 48 | } -------------------------------------------------------------------------------- /Editor/Interface/Figma.Types.Structs.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a20508fa20ef43dfb924e62827afe07d 3 | timeCreated: 1729243214 -------------------------------------------------------------------------------- /Editor/Interface/Figma.Types.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f5f4947df342cf540b908c2dd2e2a2ee 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Interface/Interface.Records.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Figma 4 | { 5 | using Attributes; 6 | 7 | record RootMetadata(bool filter, UxmlAttribute uxml, UxmlDownloadImages downloadImages); 8 | 9 | // ReSharper disable once NotAccessedPositionalProperty.Global 10 | record QueryMetadata(Type fieldType, QueryAttribute query); 11 | 12 | record BaseNodeMetadata(RootMetadata root, QueryMetadata query); 13 | } -------------------------------------------------------------------------------- /Editor/Interface/Interface.Records.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a02ced439c1547dea0ba054f881f0892 3 | timeCreated: 1727941971 -------------------------------------------------------------------------------- /License.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fdd2e558d847d528a87531ad64ba1e48 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Prefabs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c4567610e8b246a43ac8ab4dce22b73b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Prefabs/Figma.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &2916098490094507199 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 2492470867148571057} 12 | - component: {fileID: 6641805578009137364} 13 | - component: {fileID: -3369783722120399799} 14 | m_Layer: 0 15 | m_Name: Figma 16 | m_TagString: Untagged 17 | m_Icon: {fileID: 0} 18 | m_NavMeshLayer: 0 19 | m_StaticEditorFlags: 0 20 | m_IsActive: 1 21 | --- !u!4 &2492470867148571057 22 | Transform: 23 | m_ObjectHideFlags: 0 24 | m_CorrespondingSourceObject: {fileID: 0} 25 | m_PrefabInstance: {fileID: 0} 26 | m_PrefabAsset: {fileID: 0} 27 | m_GameObject: {fileID: 2916098490094507199} 28 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 29 | m_LocalPosition: {x: 0, y: 0, z: 0} 30 | m_LocalScale: {x: 1, y: 1, z: 1} 31 | m_ConstrainProportionsScale: 0 32 | m_Children: [] 33 | m_Father: {fileID: 0} 34 | m_RootOrder: 0 35 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 36 | --- !u!114 &6641805578009137364 37 | MonoBehaviour: 38 | m_ObjectHideFlags: 0 39 | m_CorrespondingSourceObject: {fileID: 0} 40 | m_PrefabInstance: {fileID: 0} 41 | m_PrefabAsset: {fileID: 0} 42 | m_GameObject: {fileID: 2916098490094507199} 43 | m_Enabled: 1 44 | m_EditorHideFlags: 0 45 | m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0} 46 | m_Name: 47 | m_EditorClassIdentifier: 48 | m_PanelSettings: {fileID: 11400000, guid: f486055d8ec653edc96c3f3c38380c8f, type: 2} 49 | m_ParentUI: {fileID: 0} 50 | sourceAsset: {fileID: 0} 51 | m_SortingOrder: 0 52 | --- !u!114 &-3369783722120399799 53 | MonoBehaviour: 54 | m_ObjectHideFlags: 0 55 | m_CorrespondingSourceObject: {fileID: 0} 56 | m_PrefabInstance: {fileID: 0} 57 | m_PrefabAsset: {fileID: 0} 58 | m_GameObject: {fileID: 2916098490094507199} 59 | m_Enabled: 1 60 | m_EditorHideFlags: 0 61 | m_Script: {fileID: 11500000, guid: aaa4e5d0dd7bf25488b120aa854832eb, type: 3} 62 | m_Name: 63 | m_EditorClassIdentifier: 64 | title: 65 | filter: 0 66 | reorder: 0 67 | fontsDirs: [] 68 | -------------------------------------------------------------------------------- /Prefabs/Figma.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a1decc1df2e53b24285e24bfd721a22c 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Readme.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 97c5af11bb3cfa4c4952aab609afcd17 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 70ee33d9a3494344ab4d51378a9f394f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable S2094 2 | 3 | using System.Runtime.CompilerServices; 4 | 5 | [assembly: InternalsVisibleTo("Figma.Editor")] 6 | 7 | namespace System.Runtime.CompilerServices 8 | { 9 | public class IsExternalInit { } 10 | } -------------------------------------------------------------------------------- /Runtime/AssemblyInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d02acb23f49d34ad996232782b6f0270 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Core.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a2887f4413b54672bad1ba5d2c6d7350 3 | timeCreated: 1728307245 -------------------------------------------------------------------------------- /Runtime/Core/Element.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UIElements; 3 | 4 | // ReSharper disable MemberCanBeProtected.Global 5 | 6 | namespace Figma 7 | { 8 | using Internals; 9 | 10 | public abstract class Element : MonoBehaviour, IRootElement 11 | { 12 | #region Fields 13 | string className; 14 | #endregion 15 | 16 | #region Properties 17 | public virtual int RootOrder => 0; 18 | public string ClassName => className.NotNullOrEmpty() ? className : GetType().Name; 19 | public VisualElement Root { get; private set; } 20 | public VisualElement[] RootsPreserved { get; private set; } 21 | #endregion 22 | 23 | #region Methods 24 | void IRootElement.OnInitialize(VisualElement root, VisualElement[] rootsPreserved) 25 | { 26 | Root = root; 27 | RootsPreserved = rootsPreserved; 28 | OnInitialize(); 29 | } 30 | void IRootElement.OnRebuild() => OnRebuild(); 31 | 32 | protected virtual void OnInitialize() { } 33 | protected virtual void OnRebuild() { } 34 | #endregion 35 | 36 | #region Base Methods 37 | protected virtual void Awake() => className = GetType().Name; 38 | protected virtual void OnEnable() => className = GetType().Name; 39 | protected virtual void OnDisable() { } 40 | #endregion 41 | } 42 | } -------------------------------------------------------------------------------- /Runtime/Core/Element.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c3d7de8bb8ca66b41a2a58e3c87acd68 3 | timeCreated: 1524533501 4 | licenseType: Store 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Runtime/Core/QueryAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using UnityEngine.UIElements; 4 | 5 | // ReSharper disable UnusedAutoPropertyAccessor.Global 6 | // ReSharper disable MemberCanBeProtected.Global 7 | 8 | namespace Figma.Attributes 9 | { 10 | [DebuggerStepThrough] 11 | [AttributeUsage(AttributeTargets.Field)] 12 | public class QueryAttribute : Attribute 13 | { 14 | #region Properties 15 | public string Path { get; } 16 | public string ClassName { get; } 17 | [Obsolete("Use 'DownloadImage' instead")] 18 | public ElementDownloadImage ImageFiltering { get => DownloadImage; set => DownloadImage = value; } 19 | public ElementDownloadImage DownloadImage { get; set; } 20 | public bool Hash { get; set; } 21 | public string ReplaceElementPath { get; set; } 22 | public string RebuildElementEvent { get; set; } 23 | public bool StartRoot { get; set; } 24 | public bool EndRoot { get; set; } 25 | public bool Nullable { get; set; } 26 | public bool Hide { get; set; } 27 | public bool Localize { get; set; } = true; 28 | public string Clicked { get; set; } 29 | public string Template { get; set; } 30 | public TrickleDown UseTrickleDown { get; set; } 31 | public string MouseCaptureOutEvent { get; set; } 32 | public string MouseCaptureEvent { get; set; } 33 | public string ChangeEvent { get; set; } 34 | public string ValidateCommandEvent { get; set; } 35 | public string ExecuteCommandEvent { get; set; } 36 | public string DragExitedEvent { get; set; } 37 | public string DragUpdatedEvent { get; set; } 38 | public string DragPerformEvent { get; set; } 39 | public string DragEnterEvent { get; set; } 40 | public string DragLeaveEvent { get; set; } 41 | public string FocusOutEvent { get; set; } 42 | public string BlurEvent { get; set; } 43 | public string FocusInEvent { get; set; } 44 | public string FocusEvent { get; set; } 45 | public string InputEvent { get; set; } 46 | public string KeyDownEvent { get; set; } 47 | public string KeyUpEvent { get; set; } 48 | public string GeometryChangedEvent { get; set; } 49 | public string PointerDownEvent { get; set; } 50 | public string PointerUpEvent { get; set; } 51 | public string PointerMoveEvent { get; set; } 52 | public string MouseDownEvent { get; set; } 53 | public string MouseUpEvent { get; set; } 54 | public string MouseMoveEvent { get; set; } 55 | public string ContextClickEvent { get; set; } 56 | public string WheelEvent { get; set; } 57 | public string MouseEnterEvent { get; set; } 58 | public string MouseLeaveEvent { get; set; } 59 | public string MouseEnterWindowEvent { get; set; } 60 | public string MouseLeaveWindowEvent { get; set; } 61 | public string MouseOverEvent { get; set; } 62 | public string MouseOutEvent { get; set; } 63 | public string ContextualMenuPopulateEvent { get; set; } 64 | public string AttachToPanelEvent { get; set; } 65 | public string DetachFromPanelEvent { get; set; } 66 | public string TooltipEvent { get; set; } 67 | public string IMGUIEvent { get; set; } 68 | #endregion 69 | 70 | #region Constructors 71 | public QueryAttribute(string path, string className = null) 72 | { 73 | Path = path; 74 | ClassName = className; 75 | } 76 | #endregion 77 | } 78 | } -------------------------------------------------------------------------------- /Runtime/Core/QueryAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ba43ed1a94fbe41469ea9d6f2b01ca1b 3 | timeCreated: 1512009160 4 | licenseType: Store 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Runtime/Core/UxmlAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Figma.Attributes 5 | { 6 | using static Internals.PathExtensions; 7 | 8 | [DebuggerStepThrough] 9 | [AttributeUsage(AttributeTargets.Class)] 10 | public class UxmlAttribute : Attribute 11 | { 12 | public const string prefix = "Document"; 13 | 14 | #region Properties 15 | public string Root { get; } 16 | public string DocumentRoot { get; } 17 | public string[] Preserve { get; } 18 | public string[] DocumentPreserve { get; } 19 | public UxmlDownloadImages DownloadImages { get; } 20 | public UxmlElementTypeIdentification TypeIdentification { get; } 21 | #endregion 22 | 23 | #region Constructors 24 | public UxmlAttribute(string root = null, UxmlDownloadImages downloadImages = UxmlDownloadImages.Everything, UxmlElementTypeIdentification typeIdentification = UxmlElementTypeIdentification.ByName, params string[] preserve) 25 | { 26 | Root = root; 27 | 28 | DocumentRoot = CombinePath(prefix, root); 29 | Preserve = preserve; 30 | DocumentPreserve = (string[])preserve.Clone(); 31 | 32 | for (int i = 0; i < preserve.Length; ++i) 33 | DocumentPreserve[i] = CombinePath(prefix, DocumentPreserve[i]); 34 | 35 | DownloadImages = downloadImages; 36 | TypeIdentification = typeIdentification; 37 | } 38 | #endregion 39 | } 40 | } -------------------------------------------------------------------------------- /Runtime/Core/UxmlAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c1215fc5876343f49f128b37e1181924 3 | timeCreated: 1728307231 -------------------------------------------------------------------------------- /Runtime/Core/VisualElementMetadata.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 46d8035533165cc498ba4642614403d3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5919892104fee434aa143c8b3a125a90 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using System.Collections.Generic; 6 | 7 | namespace Figma 8 | { 9 | internal static class EnumerableExtensions 10 | { 11 | #region Methods 12 | internal static async Task ForEachParallelAsync(this IEnumerable elements, int maxConcurrentRequests, Func func, CancellationToken token) 13 | { 14 | using SemaphoreSlim semaphore = new(maxConcurrentRequests); 15 | Task[] tasks = elements.Select(async x => 16 | { 17 | await semaphore.WaitAsync(token); 18 | await func(x); 19 | semaphore.Release(); 20 | }).ToArray(); 21 | await Task.WhenAll(tasks); 22 | } 23 | internal static IEnumerable> Chunk(this IEnumerable source, int size) 24 | { 25 | using IEnumerator enumerator = source.GetEnumerator(); 26 | bool hasMoreElements = true; 27 | 28 | do 29 | { 30 | List chunk = new(size); 31 | while (size > chunk.Count && (hasMoreElements = enumerator.MoveNext())) 32 | chunk.Add(enumerator.Current); 33 | if (chunk.Count == 0) 34 | break; 35 | yield return chunk; 36 | } while (hasMoreElements); 37 | } 38 | #endregion 39 | } 40 | } -------------------------------------------------------------------------------- /Runtime/Extensions/EnumerableExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a0dc8b85c82ec86fa8aa435da7d2c758 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Extensions/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | 5 | namespace Figma.Internals 6 | { 7 | [DebuggerStepThrough] 8 | internal static class Extensions 9 | { 10 | #region Methods 11 | internal static bool NullOrEmpty(this string value) => string.IsNullOrEmpty(value); 12 | internal static bool NotNullOrEmpty(this string value) => !string.IsNullOrEmpty(value); 13 | internal static bool Invalid(this float value) => float.IsNaN(value) || float.IsInfinity(value); 14 | internal static void ForEach(this IEnumerable enumerable, Action action) 15 | { 16 | foreach (T element in enumerable) 17 | action(element); 18 | } 19 | #if UNITY_EDITOR 20 | internal static string BuildTargetMessage(string message, string target, string end = null) => $"{message} [{target}] {end ?? string.Empty}"; 21 | #else 22 | internal static string BuildTargetMessage(string message, string target, string end = null) => $"{message} [{target}] {end ?? string.Empty}"; 23 | #endif 24 | #endregion 25 | } 26 | } -------------------------------------------------------------------------------- /Runtime/Extensions/Extensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f333cb303f7c42e589d3a706dade2e0a 3 | timeCreated: 1727960585 -------------------------------------------------------------------------------- /Runtime/Extensions/PathExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Figma.Internals 7 | { 8 | public static class PathExtensions 9 | { 10 | #region Const 11 | public const char pathSeparator = '/'; 12 | #endregion 13 | 14 | #region Methods 15 | internal static string[] GetFiles(string path, string searchPattern, SearchOption searchOption) => Directory.GetFiles(path, searchPattern, searchOption).Select(x => x.Replace('\\', pathSeparator)).ToArray(); 16 | internal static string CombinePath(params string[] paths) 17 | { 18 | if (paths == null || paths.Length == 0) 19 | throw new ArgumentNullException(nameof(paths)); 20 | 21 | StringBuilder path = new(); 22 | 23 | for (int i = 0; i < paths.Length; i++) 24 | { 25 | if (string.IsNullOrEmpty(paths[i])) 26 | continue; 27 | 28 | path.Append(paths[i].Replace('\\', pathSeparator)); 29 | 30 | if (i < paths.Length - 1 && paths[i][paths[i].Length - 1] != pathSeparator && !string.IsNullOrEmpty(paths[i + 1])) 31 | path.Append(pathSeparator); 32 | } 33 | 34 | return path.ToString(); 35 | } 36 | internal static string GetRelativePath(string from, string to) => CombinePath(Path.GetRelativePath(Path.GetDirectoryName(from), Path.GetDirectoryName(to))?.Replace('\\', pathSeparator), Path.GetFileName(to)); 37 | internal static string RemoveExtension(string path) => CombinePath(Path.GetDirectoryName(path)?.Replace('\\', pathSeparator), Path.GetFileNameWithoutExtension(path)); 38 | 39 | internal static bool IsSeparator(this char ch) => ch == Path.DirectorySeparatorChar || ch == Path.AltDirectorySeparatorChar; 40 | internal static bool EqualsTo(this string path, string value, int startIndex = 0) 41 | { 42 | if (path.Length - startIndex != value.Length) return false; 43 | 44 | int i, length = value.Length; 45 | for (i = 0; i < length; ++i) 46 | { 47 | if (path[startIndex + i].IsSeparator() && value[i].IsSeparator() || path[startIndex + i] == value[i]) 48 | continue; 49 | 50 | return false; 51 | } 52 | 53 | return i == length; 54 | } 55 | internal static bool BeginsWith(this string path, string value, int startIndex = 0) 56 | { 57 | if (path.Length - startIndex < value.Length) 58 | return false; 59 | 60 | int i, length; 61 | for (i = 0, length = value.Length; i < length; ++i) 62 | { 63 | if (path[startIndex + i].IsSeparator() && value[i].IsSeparator() || path[startIndex + i] == value[i]) 64 | continue; 65 | 66 | return false; 67 | } 68 | 69 | return startIndex + i == path.Length || path[startIndex + i].IsSeparator(); 70 | } 71 | #endregion 72 | } 73 | } -------------------------------------------------------------------------------- /Runtime/Extensions/PathExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7967c428b3ee1604e8b5050a79c3278a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Extensions/VisualElementExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine.UIElements; 5 | 6 | namespace Figma 7 | { 8 | public static class VisualElementExtensions 9 | { 10 | #region Fields 11 | internal static readonly Dictionary<(VisualElement prefab, VisualElement parent), IList> cloneDictionary = new(); 12 | #endregion 13 | 14 | #region Methods 15 | public static bool HasVisibility(this VisualElement element) => element.style.visibility == Visibility.Visible; 16 | public static void MakeVisible(this VisualElement element) => element.style.visibility = Visibility.Visible; 17 | public static void MakeInvisible(this VisualElement element) => element.style.visibility = Visibility.Hidden; 18 | public static void SetVisibility(this VisualElement element, bool visible) => element.style.visibility = visible ? Visibility.Visible : Visibility.Hidden; 19 | public static bool IsShowing(this VisualElement element) => element.resolvedStyle.display == DisplayStyle.Flex; 20 | public static void Show(this VisualElement element) 21 | { 22 | element.style.display = DisplayStyle.Flex; 23 | element.MarginMe(); 24 | } 25 | public static void Hide(this VisualElement element) => element.style.display = DisplayStyle.None; 26 | public static void SetDisplay(this VisualElement element, bool visible) 27 | { 28 | element.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None; 29 | if (visible) element.MarginMe(); 30 | } 31 | public static void Disable(this VisualElement element) => element.pickingMode = PickingMode.Ignore; 32 | public static void Enable(this VisualElement element) => element.pickingMode = PickingMode.Position; 33 | public static bool IsEnabled(this VisualElement element) => element.pickingMode == PickingMode.Position; 34 | public static IList EnsureList(TVisualElement prefab, VisualElement parent) where TVisualElement : VisualElement 35 | { 36 | (TVisualElement prefab, VisualElement parent) identifier = (prefab, parent); 37 | 38 | if (!cloneDictionary.TryGetValue(identifier, out IList elements)) 39 | cloneDictionary.Add(identifier, elements = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(prefab.GetType()))); 40 | 41 | return elements; 42 | } 43 | public static TVisualElement GetElement(this TVisualElement prefab, int index) where TVisualElement : VisualElement => GetElements(prefab, prefab.parent)[index]; 44 | public static List GetElements(this TVisualElement prefab) where TVisualElement : VisualElement => GetElements(prefab, prefab.parent); 45 | public static List GetElements(this TVisualElement prefab, VisualElement parent) where TVisualElement : VisualElement 46 | { 47 | if (EnsureList(prefab, parent) is List list) return list; 48 | 49 | throw new ArgumentException($"Casting from {typeof(List)} to {cloneDictionary[(prefab, parent)]}"); 50 | } 51 | public static List GetElements(this VisualElement prefab) => GetElements(prefab); 52 | public static List GetElements(this VisualElement prefab, VisualElement parent) => GetElements(prefab, parent); 53 | public static void Sync(this TVisualElement prefab, VisualElement parent, IEnumerable data, Action onCreateElement = null) where TVisualElement : VisualElement, ISyncElement 54 | { 55 | IList elements = EnsureList(prefab, parent); 56 | 57 | int i = 0; 58 | foreach (TData value in data) 59 | { 60 | TVisualElement element; 61 | if (i >= elements.Count) 62 | { 63 | element = prefab.Clone(parent, i); 64 | element.Initialize(i); 65 | onCreateElement?.Invoke(element); 66 | elements.Add(element); 67 | } 68 | else 69 | { 70 | element = (TVisualElement)elements[i]; 71 | } 72 | 73 | if (element.IsVisible(i, value)) element.Show(); 74 | else element.Hide(); 75 | 76 | ++i; 77 | } 78 | 79 | for (int j = i; j < elements.Count; ++j) elements[j].As().Hide(); 80 | } 81 | public static void Sync(this TVisualElement prefab, VisualElement parent, TCreationData creationData, IEnumerable data, Action onCreateElement = null) where TVisualElement : VisualElement, ISyncElement 82 | { 83 | IList elements = EnsureList(prefab, parent); 84 | 85 | int i = 0; 86 | foreach (TData value in data) 87 | { 88 | TVisualElement element; 89 | if (i >= elements.Count) 90 | { 91 | element = prefab.Clone(parent, i); 92 | element.Initialize(i, creationData); 93 | onCreateElement?.Invoke(element); 94 | elements.Add(element); 95 | } 96 | else 97 | { 98 | element = (TVisualElement)elements[i]; 99 | } 100 | 101 | if (element.IsVisible(i, value)) element.Show(); 102 | else element.Hide(); 103 | 104 | ++i; 105 | } 106 | 107 | for (int j = i; j < elements.Count; ++j) elements[j].As().Hide(); 108 | } 109 | public static T As(this object value) => (T)value; 110 | #endregion 111 | } 112 | } -------------------------------------------------------------------------------- /Runtime/Extensions/VisualElementExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 84fecb8572fb586409235bea58b5d37e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Figma.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Figma", 3 | "rootNamespace": "Figma", 4 | "references": [ 5 | "AsyncAwaitUtil" 6 | ] 7 | } -------------------------------------------------------------------------------- /Runtime/Figma.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8b8c60060cf03964dbd41b7237ae33c9 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Figma.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using UnityEngine; 3 | using UnityEngine.UIElements; 4 | 5 | namespace Figma 6 | { 7 | using Attributes; 8 | 9 | [DefaultExecutionOrder(-10)] 10 | [RequireComponent(typeof(UIDocument))] 11 | public class Figma : MonoBehaviour 12 | { 13 | #region Fields 14 | [SerializeField] string fileKey; 15 | [SerializeField] bool filter; 16 | [SerializeField] bool reorder; 17 | [SerializeField] bool waitFrameBeforeRebuild = true; 18 | [SerializeField] string[] fontDirectories; 19 | #endregion 20 | 21 | #region Properties 22 | public string FileKey => fileKey; 23 | public bool Filter => filter; 24 | #endregion 25 | 26 | #region Base Methods 27 | async void OnEnable() 28 | { 29 | if (Application.isBatchMode) 30 | return; 31 | 32 | UIDocument document = GetComponent(); 33 | 34 | if (document.rootVisualElement is null) 35 | return; 36 | 37 | IRootElement[] elements = GetComponentsInChildren(); 38 | VisualElementMetadata.Initialize(document, elements); 39 | 40 | if (!Application.isPlaying) 41 | return; 42 | 43 | // Do not change this to Awaiters, since it is breaking the loading. 44 | if (waitFrameBeforeRebuild) 45 | await new WaitForEndOfFrame(); 46 | 47 | VisualElementMetadata.Rebuild(elements); 48 | 49 | VisualElement root = document.rootVisualElement.Q(UxmlAttribute.prefix); 50 | 51 | if (root is null || !reorder) 52 | return; 53 | 54 | foreach (IRootElement element in elements.OrderBy(x => x.RootOrder)) 55 | { 56 | if (element.Root is null) 57 | continue; 58 | 59 | element.Root.RemoveFromHierarchy(); 60 | root.Add(element.Root); 61 | } 62 | } 63 | void OnDestroy() => VisualElementMetadata.Dispose(); 64 | #endregion 65 | } 66 | } -------------------------------------------------------------------------------- /Runtime/Figma.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aaa4e5d0dd7bf25488b120aa854832eb 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Interface.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1f0b9b39faba9904dbefcb7c550f313d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Interface/Core.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4f9a8b3c90b78d644a304baf92e4c76d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Interface/Core/SubElements.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine.UIElements; 2 | 3 | namespace Figma 4 | { 5 | public abstract class SubVisualElement : VisualElement, ISubElement 6 | { 7 | #region Methods 8 | protected virtual void OnInitialize() { } 9 | protected virtual void OnRebuild() { } 10 | 11 | void ISubElement.OnInitialize() => OnInitialize(); 12 | void ISubElement.OnRebuild() => OnRebuild(); 13 | #endregion 14 | } 15 | 16 | public abstract class SubButton : Button, ISubElement 17 | { 18 | #region Methods 19 | protected virtual void OnInitialize() { } 20 | protected virtual void OnRebuild() { } 21 | 22 | void ISubElement.OnInitialize() => OnInitialize(); 23 | void ISubElement.OnRebuild() => OnRebuild(); 24 | #endregion 25 | } 26 | 27 | public abstract class SubLabel : Label, ISubElement 28 | { 29 | #region Methods 30 | protected virtual void OnInitialize() { } 31 | protected virtual void OnRebuild() { } 32 | 33 | void ISubElement.OnInitialize() => OnInitialize(); 34 | void ISubElement.OnRebuild() => OnRebuild(); 35 | #endregion 36 | } 37 | 38 | public abstract class SubScrollView : ScrollView, ISubElement 39 | { 40 | #region Methods 41 | protected virtual void OnInitialize() { } 42 | protected virtual void OnRebuild() { } 43 | 44 | void ISubElement.OnInitialize() => OnInitialize(); 45 | void ISubElement.OnRebuild() => OnRebuild(); 46 | #endregion 47 | } 48 | } -------------------------------------------------------------------------------- /Runtime/Interface/Core/SubElements.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 99f85b01fae873ffdac3884951113a85 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Interface/Core/SyncElements.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine.UIElements; 4 | 5 | namespace Figma 6 | { 7 | public abstract class SyncVisualElement : SubVisualElement, ISyncElement 8 | { 9 | #region Methods 10 | public virtual void Sync(VisualElement parent, IEnumerable data) => VisualElementExtensions.Sync(this, parent, data); 11 | public abstract bool IsVisible(int index, T data); 12 | #endregion 13 | } 14 | 15 | public abstract class SyncVisualElement : SubVisualElement, ISyncElement 16 | { 17 | #region Methods 18 | public virtual void Sync(VisualElement parent, TCreationData creationData, IEnumerable data) => VisualElementExtensions.Sync(this, parent, creationData, data); 19 | public abstract void Initialize(int index, TCreationData creationData); 20 | public abstract bool IsVisible(int index, TData data); 21 | #endregion 22 | } 23 | 24 | public abstract class SyncButton : SubButton, ISyncElement 25 | { 26 | #region Methods 27 | public virtual void Sync(VisualElement parent, IEnumerable data) => VisualElementExtensions.Sync(this, parent, data); 28 | public abstract bool IsVisible(int index, TData data); 29 | #endregion 30 | } 31 | 32 | public abstract class SyncButton : SubButton, ISyncElement 33 | { 34 | #region Methods 35 | public virtual void Sync(VisualElement parent, TCreationData creationData, IEnumerable data) => VisualElementExtensions.Sync(this, parent, creationData, data); 36 | public abstract void Initialize(int index, TCreationData creationData); 37 | public abstract bool IsVisible(int index, TData data); 38 | #endregion 39 | } 40 | 41 | public abstract class SyncButtonSimple : SubButton, ISyncElement, TData> 42 | { 43 | #region Methods 44 | public virtual void Sync(VisualElement parent, Action creationData, IEnumerable data) => VisualElementExtensions.Sync(this, parent, creationData, data); 45 | public virtual void Initialize(int index, Action creationData) => clicked += () => creationData(index); 46 | public abstract bool IsVisible(int index, TData data); 47 | #endregion 48 | } 49 | 50 | public abstract class SyncLabel : SubLabel, ISyncElement 51 | { 52 | #region Methods 53 | public virtual void Sync(VisualElement parent, IEnumerable data) => VisualElementExtensions.Sync(this, parent, data); 54 | public abstract bool IsVisible(int index, TData data); 55 | #endregion 56 | } 57 | 58 | public abstract class SyncLabel : SubLabel, ISyncElement 59 | { 60 | #region Methods 61 | public virtual void Sync(VisualElement parent, TCreationData creationData, IEnumerable data) => VisualElementExtensions.Sync(this, parent, creationData, data); 62 | public abstract void Initialize(int index, TCreationData creationData); 63 | public abstract bool IsVisible(int index, TData data); 64 | #endregion 65 | } 66 | 67 | public abstract class SyncScrollView : SubScrollView, ISyncElement 68 | { 69 | #region Methods 70 | public virtual void Sync(VisualElement parent, IEnumerable data) => VisualElementExtensions.Sync(this, parent, data); 71 | public abstract bool IsVisible(int index, TData data); 72 | #endregion 73 | } 74 | 75 | public abstract class SyncScrollView : SubScrollView, ISyncElement 76 | { 77 | #region Methods 78 | public virtual void Sync(VisualElement parent, TCreationData creationData, IEnumerable data) => VisualElementExtensions.Sync(this, parent, creationData, data); 79 | public abstract void Initialize(int index, TCreationData creationData); 80 | public abstract bool IsVisible(int index, TData data); 81 | #endregion 82 | } 83 | } -------------------------------------------------------------------------------- /Runtime/Interface/Core/SyncElements.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fd496d4ec1c37d7af931d627ebf86023 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Interface/Enums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Figma 4 | { 5 | [Flags] 6 | public enum UxmlDownloadImages 7 | { 8 | Everything = 0, 9 | Nothing = 1 << 0, 10 | ImageFills = 1 << 1, 11 | RenderAsPng = 1 << 2, 12 | RenderAsSvg = 1 << 3, 13 | ByElements = 1 << 4 14 | } 15 | 16 | public enum UxmlElementTypeIdentification 17 | { 18 | ByName, 19 | ByElementType 20 | } 21 | 22 | public enum ElementDownloadImage 23 | { 24 | Auto, 25 | Download, 26 | Ignore 27 | } 28 | 29 | [Flags] 30 | public enum CopyStyleMask 31 | { 32 | None = 0, 33 | Text = 1 << 0, 34 | Position = 1 << 1, 35 | Size = 1 << 2, 36 | Flex = 1 << 3, 37 | Display = 1 << 4, 38 | Padding = 1 << 5, 39 | Margins = 1 << 6, 40 | Borders = 1 << 7, 41 | Slicing = 1 << 8, 42 | Font = 1 << 9, 43 | All = Text | Position | Size | Flex | Display | Padding | Margins | Borders | Slicing | Font 44 | } 45 | } -------------------------------------------------------------------------------- /Runtime/Interface/Enums.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 801e83821cb9e5f408837586af0e8d8f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Interface/Interface.Core.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine.UIElements; 3 | 4 | #pragma warning disable S1186 // Functions and closures should not be empty 5 | 6 | namespace Figma 7 | { 8 | public interface ISyncElement 9 | { 10 | #region Methods 11 | void Sync(VisualElement parent, IEnumerable data); 12 | void Initialize(int index) { } 13 | bool IsVisible(int index, TData data); 14 | #endregion 15 | } 16 | 17 | public interface ISyncElement 18 | { 19 | #region Methods 20 | void Sync(VisualElement parent, TCreationData creationData, IEnumerable data); 21 | void Initialize(int index, TCreationData creationData); 22 | bool IsVisible(int index, TData data); 23 | #endregion 24 | } 25 | } -------------------------------------------------------------------------------- /Runtime/Interface/Interface.Core.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0162e75c60e15fd4a9bd4da8ac0c62ed 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Interface/Interfaces.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine.UIElements; 2 | 3 | namespace Figma 4 | { 5 | public interface ISubElement 6 | { 7 | #region Methods 8 | void OnInitialize() { } // Method is blank intentionally 9 | void OnRebuild() { } // Method is blank intentionally 10 | #endregion 11 | } 12 | 13 | public interface IRootElement 14 | { 15 | #region Properties 16 | VisualElement Root { get; } 17 | int RootOrder => 0; 18 | #endregion 19 | 20 | #region Methods 21 | void OnInitialize(VisualElement root, VisualElement[] rootsPreserved) { } // Method is blank intentionally 22 | void OnRebuild() { } // Method is blank intentionally 23 | #endregion 24 | } 25 | } -------------------------------------------------------------------------------- /Runtime/Interface/Interfaces.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e01966bb91b67f341809945497f0ff2c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.trackman.figma", 3 | "description": "Figma to Unity uxml/uss converter, utilities for VisualTree management in runtime.", 4 | "displayName": "Figma for Unity", 5 | "version": "1.2.8", 6 | "unity": "2022.3", 7 | "dependencies": { 8 | "com.unity.vectorgraphics": "2.0.0-preview.24", 9 | "com.trackman.asyncawaitutil": "1.0.6" 10 | }, 11 | "author": "TrackMan", 12 | "type": "trackman.package", 13 | "hideInEditor": false 14 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8054faacce927e54296a8be216631c8e 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /~Samples.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f562b0f5dfae4abab3176961a5070e1f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /~Samples/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a8bf1e60512441341bdf13c14f151aef 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /~Samples/Scripts/Figma.Samples.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Figma.Samples", 3 | "rootNamespace": "", 4 | "references": [ 5 | "Figma" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /~Samples/Scripts/Figma.Samples.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 40458b32386acba81b5009a08b0c7d40 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /~Samples/Scripts/Test.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using UnityEngine.UIElements; 3 | using Random = UnityEngine.Random; 4 | 5 | namespace Figma.Samples 6 | { 7 | using Attributes; 8 | 9 | [Uxml("TestPage/TestFrame", UxmlDownloadImages.Everything, UxmlElementTypeIdentification.ByElementType)] 10 | public class Test : Element 11 | { 12 | const int minCircles = 1; 13 | const int maxCircles = 7; 14 | 15 | #region Fields 16 | [Query("Header")] Label header; 17 | 18 | [Query("CloneButton", Clicked = nameof(Clone))] Button cloneButton; 19 | [Query("RemoveButton", Clicked = nameof(Remove))] Button removeButton; 20 | [Query("CloneContainer", StartRoot = true)] VisualElement cloneContainer; 21 | [Query("CloneCircle", EndRoot = true)] PerfectCircle cloneCircle; 22 | 23 | [Query("SyncButton", Clicked = nameof(Sync))] Button syncButton; 24 | [Query("SyncContainer")] VisualElement syncContainer; 25 | [Query("SyncContainer/SyncCircle")] PerfectCircle syncCircle; 26 | 27 | [Query("FunctionDescription", Hide = true)] Label functionDescription; 28 | #endregion 29 | 30 | #region Methods 31 | protected override void OnInitialize() => cloneContainer.style.flexWrap = Wrap.NoWrap; 32 | protected override void OnRebuild() => header.text = "Welcome to Figma Test Frame!"; 33 | 34 | void Clone() 35 | { 36 | if (cloneContainer.childCount == maxCircles) return; 37 | 38 | cloneCircle.Clone(cloneContainer); 39 | } 40 | void Remove() 41 | { 42 | if (cloneContainer.childCount == minCircles) return; 43 | 44 | cloneContainer.Remove(cloneContainer.Children().First()); 45 | } 46 | void Sync() 47 | { 48 | void RandomColor(int index) => syncContainer.Children().ElementAt(index).style.backgroundColor = Random.ColorHSV(); 49 | 50 | syncCircle.Sync(syncContainer, RandomColor, Enumerable.Range(0, Random.Range(1, maxCircles + 1))); 51 | syncCircle.Hide(); 52 | 53 | functionDescription.Show(); 54 | } 55 | #endregion 56 | } 57 | 58 | public class PerfectCircle : SyncButtonSimple 59 | { 60 | public new class UxmlFactory : UxmlFactory { } 61 | 62 | #region Methods 63 | public override bool IsVisible(int index, int data) => true; 64 | #endregion 65 | } 66 | } -------------------------------------------------------------------------------- /~Samples/Scripts/Test.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 51bc2941ace5c496fbf512e17e9a1939 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /~Samples/Test.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} 24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} 25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 0 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 0} 41 | m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} 42 | m_UseRadianceAmbientProbe: 0 43 | --- !u!157 &3 44 | LightmapSettings: 45 | m_ObjectHideFlags: 0 46 | serializedVersion: 12 47 | m_GIWorkflowMode: 1 48 | m_GISettings: 49 | serializedVersion: 2 50 | m_BounceScale: 1 51 | m_IndirectOutputScale: 1 52 | m_AlbedoBoost: 1 53 | m_EnvironmentLightingMode: 0 54 | m_EnableBakedLightmaps: 1 55 | m_EnableRealtimeLightmaps: 0 56 | m_LightmapEditorSettings: 57 | serializedVersion: 12 58 | m_Resolution: 2 59 | m_BakeResolution: 40 60 | m_AtlasSize: 1024 61 | m_AO: 0 62 | m_AOMaxDistance: 1 63 | m_CompAOExponent: 1 64 | m_CompAOExponentDirect: 0 65 | m_ExtractAmbientOcclusion: 0 66 | m_Padding: 2 67 | m_LightmapParameters: {fileID: 0} 68 | m_LightmapsBakeMode: 1 69 | m_TextureCompression: 1 70 | m_FinalGather: 0 71 | m_FinalGatherFiltering: 1 72 | m_FinalGatherRayCount: 256 73 | m_ReflectionCompression: 2 74 | m_MixedBakeMode: 2 75 | m_BakeBackend: 1 76 | m_PVRSampling: 1 77 | m_PVRDirectSampleCount: 32 78 | m_PVRSampleCount: 512 79 | m_PVRBounces: 2 80 | m_PVREnvironmentSampleCount: 256 81 | m_PVREnvironmentReferencePointCount: 2048 82 | m_PVRFilteringMode: 1 83 | m_PVRDenoiserTypeDirect: 1 84 | m_PVRDenoiserTypeIndirect: 1 85 | m_PVRDenoiserTypeAO: 1 86 | m_PVRFilterTypeDirect: 0 87 | m_PVRFilterTypeIndirect: 0 88 | m_PVRFilterTypeAO: 0 89 | m_PVREnvironmentMIS: 1 90 | m_PVRCulling: 1 91 | m_PVRFilteringGaussRadiusDirect: 1 92 | m_PVRFilteringGaussRadiusIndirect: 5 93 | m_PVRFilteringGaussRadiusAO: 2 94 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 95 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 96 | m_PVRFilteringAtrousPositionSigmaAO: 1 97 | m_ExportTrainingData: 0 98 | m_TrainingDataDestination: TrainingData 99 | m_LightProbeSampleCountMultiplier: 4 100 | m_LightingDataAsset: {fileID: 0} 101 | m_LightingSettings: {fileID: 0} 102 | --- !u!196 &4 103 | NavMeshSettings: 104 | serializedVersion: 2 105 | m_ObjectHideFlags: 0 106 | m_BuildSettings: 107 | serializedVersion: 2 108 | agentTypeID: 0 109 | agentRadius: 0.5 110 | agentHeight: 2 111 | agentSlope: 45 112 | agentClimb: 0.4 113 | ledgeDropHeight: 0 114 | maxJumpAcrossDistance: 0 115 | minRegionArea: 2 116 | manualCellSize: 0 117 | cellSize: 0.16666667 118 | manualTileSize: 0 119 | tileSize: 256 120 | accuratePlacement: 0 121 | maxJobWorkers: 0 122 | preserveTilesOutsideBounds: 0 123 | debug: 124 | m_Flags: 0 125 | m_NavMeshData: {fileID: 0} 126 | --- !u!1001 &1931369715 127 | PrefabInstance: 128 | m_ObjectHideFlags: 0 129 | serializedVersion: 2 130 | m_Modification: 131 | m_TransformParent: {fileID: 0} 132 | m_Modifications: 133 | - target: {fileID: -3369783722120399799, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 134 | propertyPath: title 135 | value: 11EfaZFWQFIRh68VYRJMBo 136 | objectReference: {fileID: 0} 137 | - target: {fileID: -3369783722120399799, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 138 | propertyPath: filter 139 | value: 1 140 | objectReference: {fileID: 0} 141 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 142 | propertyPath: m_RootOrder 143 | value: 1 144 | objectReference: {fileID: 0} 145 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 146 | propertyPath: m_LocalPosition.x 147 | value: 0 148 | objectReference: {fileID: 0} 149 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 150 | propertyPath: m_LocalPosition.y 151 | value: 0 152 | objectReference: {fileID: 0} 153 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 154 | propertyPath: m_LocalPosition.z 155 | value: 0 156 | objectReference: {fileID: 0} 157 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 158 | propertyPath: m_LocalRotation.w 159 | value: 1 160 | objectReference: {fileID: 0} 161 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 162 | propertyPath: m_LocalRotation.x 163 | value: 0 164 | objectReference: {fileID: 0} 165 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 166 | propertyPath: m_LocalRotation.y 167 | value: 0 168 | objectReference: {fileID: 0} 169 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 170 | propertyPath: m_LocalRotation.z 171 | value: 0 172 | objectReference: {fileID: 0} 173 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 174 | propertyPath: m_LocalEulerAnglesHint.x 175 | value: 0 176 | objectReference: {fileID: 0} 177 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 178 | propertyPath: m_LocalEulerAnglesHint.y 179 | value: 0 180 | objectReference: {fileID: 0} 181 | - target: {fileID: 2492470867148571057, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 182 | propertyPath: m_LocalEulerAnglesHint.z 183 | value: 0 184 | objectReference: {fileID: 0} 185 | - target: {fileID: 2916098490094507199, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 186 | propertyPath: m_Name 187 | value: Figma 188 | objectReference: {fileID: 0} 189 | - target: {fileID: 6641805578009137364, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 190 | propertyPath: sourceAsset 191 | value: 192 | objectReference: {fileID: 9197481963319205126, guid: 6b8f61c2c55889e47baab4478eacf725, type: 3} 193 | - target: {fileID: 6641805578009137364, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 194 | propertyPath: m_PanelSettings 195 | value: 196 | objectReference: {fileID: 11400000, guid: f486055d8ec653edc96c3f3c38380c8f, type: 2} 197 | m_RemovedComponents: [] 198 | m_SourcePrefab: {fileID: 100100000, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 199 | --- !u!1 &2022604971 stripped 200 | GameObject: 201 | m_CorrespondingSourceObject: {fileID: 2916098490094507199, guid: a1decc1df2e53b24285e24bfd721a22c, type: 3} 202 | m_PrefabInstance: {fileID: 1931369715} 203 | m_PrefabAsset: {fileID: 0} 204 | --- !u!114 &2022604975 205 | MonoBehaviour: 206 | m_ObjectHideFlags: 0 207 | m_CorrespondingSourceObject: {fileID: 0} 208 | m_PrefabInstance: {fileID: 0} 209 | m_PrefabAsset: {fileID: 0} 210 | m_GameObject: {fileID: 2022604971} 211 | m_Enabled: 1 212 | m_EditorHideFlags: 0 213 | m_Script: {fileID: 11500000, guid: 51bc2941ace5c496fbf512e17e9a1939, type: 3} 214 | m_Name: 215 | m_EditorClassIdentifier: 216 | --- !u!1 &2089345444 217 | GameObject: 218 | m_ObjectHideFlags: 0 219 | m_CorrespondingSourceObject: {fileID: 0} 220 | m_PrefabInstance: {fileID: 0} 221 | m_PrefabAsset: {fileID: 0} 222 | serializedVersion: 6 223 | m_Component: 224 | - component: {fileID: 2089345447} 225 | - component: {fileID: 2089345446} 226 | - component: {fileID: 2089345445} 227 | m_Layer: 0 228 | m_Name: Camera 229 | m_TagString: Untagged 230 | m_Icon: {fileID: 0} 231 | m_NavMeshLayer: 0 232 | m_StaticEditorFlags: 0 233 | m_IsActive: 1 234 | --- !u!81 &2089345445 235 | AudioListener: 236 | m_ObjectHideFlags: 0 237 | m_CorrespondingSourceObject: {fileID: 0} 238 | m_PrefabInstance: {fileID: 0} 239 | m_PrefabAsset: {fileID: 0} 240 | m_GameObject: {fileID: 2089345444} 241 | m_Enabled: 1 242 | --- !u!20 &2089345446 243 | Camera: 244 | m_ObjectHideFlags: 0 245 | m_CorrespondingSourceObject: {fileID: 0} 246 | m_PrefabInstance: {fileID: 0} 247 | m_PrefabAsset: {fileID: 0} 248 | m_GameObject: {fileID: 2089345444} 249 | m_Enabled: 1 250 | serializedVersion: 2 251 | m_ClearFlags: 1 252 | m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} 253 | m_projectionMatrixMode: 1 254 | m_GateFitMode: 2 255 | m_FOVAxisMode: 0 256 | m_SensorSize: {x: 36, y: 24} 257 | m_LensShift: {x: 0, y: 0} 258 | m_FocalLength: 50 259 | m_NormalizedViewPortRect: 260 | serializedVersion: 2 261 | x: 0 262 | y: 0 263 | width: 1 264 | height: 1 265 | near clip plane: 0.3 266 | far clip plane: 1000 267 | field of view: 60 268 | orthographic: 0 269 | orthographic size: 5 270 | m_Depth: 0 271 | m_CullingMask: 272 | serializedVersion: 2 273 | m_Bits: 4294967295 274 | m_RenderingPath: -1 275 | m_TargetTexture: {fileID: 0} 276 | m_TargetDisplay: 0 277 | m_TargetEye: 3 278 | m_HDR: 1 279 | m_AllowMSAA: 1 280 | m_AllowDynamicResolution: 0 281 | m_ForceIntoRT: 0 282 | m_OcclusionCulling: 1 283 | m_StereoConvergence: 10 284 | m_StereoSeparation: 0.022 285 | --- !u!4 &2089345447 286 | Transform: 287 | m_ObjectHideFlags: 0 288 | m_CorrespondingSourceObject: {fileID: 0} 289 | m_PrefabInstance: {fileID: 0} 290 | m_PrefabAsset: {fileID: 0} 291 | m_GameObject: {fileID: 2089345444} 292 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 293 | m_LocalPosition: {x: 170.51552, y: 711.87164, z: 653.8373} 294 | m_LocalScale: {x: 1, y: 1, z: 1} 295 | m_ConstrainProportionsScale: 0 296 | m_Children: [] 297 | m_Father: {fileID: 0} 298 | m_RootOrder: 0 299 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 300 | -------------------------------------------------------------------------------- /~Samples/Test.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 37c53d635728f3fe3aa97b1d07aa4a2a 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------