diff --git a/plugins/Figma/v1/configValidation.json b/plugins/Figma/v1/configValidation.json new file mode 100644 index 00000000..707649de --- /dev/null +++ b/plugins/Figma/v1/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "Authenticate", + "dataStream": { "name": "teamProjects" }, + "required": true, + "error": "Could not connect to Figma. Check that your Personal Access Token is valid and has the required scopes, and that the Team ID is correct.", + "success": "Connected to Figma successfully." + } + ] +} diff --git a/plugins/Figma/v1/custom_types.json b/plugins/Figma/v1/custom_types.json new file mode 100644 index 00000000..8c35c7f4 --- /dev/null +++ b/plugins/Figma/v1/custom_types.json @@ -0,0 +1,23 @@ +[ + { + "name": "Figma Project", + "sourceType": "Figma Project", + "icon": "folder", + "singular": "Project", + "plural": "Projects" + }, + { + "name": "Figma Component", + "sourceType": "Figma Component", + "icon": "puzzle-piece", + "singular": "Component", + "plural": "Components" + }, + { + "name": "Figma Style", + "sourceType": "Figma Style", + "icon": "palette", + "singular": "Style", + "plural": "Styles" + } +] diff --git a/plugins/Figma/v1/dataStreams/fileComments.json b/plugins/Figma/v1/dataStreams/fileComments.json new file mode 100644 index 00000000..8f7c9c4e --- /dev/null +++ b/plugins/Figma/v1/dataStreams/fileComments.json @@ -0,0 +1,54 @@ +{ + "name": "fileComments", + "displayName": "File Comments", + "description": "Comments on a specific Figma file", + "tags": ["Files", "Comments"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "files/{{fileKey}}/comments", + "pathToData": "comments", + "expandInnerObjects": true + }, + "matches": "none", + "ui": [ + { + "name": "project", + "type": "autocomplete", + "label": "Project", + "placeholder": "Select a Figma project", + "validation": { "required": true, "message": "You must select a project" }, + "allowCustomValues": false, + "isMulti": false, + "data": { "source": "dataStream", "dataStreamName": "projectsPicker", "dataSourceConfig": {} } + }, + { + "name": "fileKey", + "type": "autocomplete", + "label": "File", + "placeholder": "Select a file", + "validation": { "required": true, "message": "You must select a file" }, + "allowCustomValues": false, + "isMulti": false, + "data": { "source": "dataStream", "dataStreamName": "filesInProject", "dataSourceConfig": { "endpointPath": { "fieldName": "project", "required": true } } } + } + ], + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "message", "displayName": "Message", "shape": "string", "role": "label" }, + { "name": "user.handle", "displayName": "Author", "shape": "string" }, + { "name": "user.id", "visible": false }, + { "name": "user.img_url", "visible": false }, + { "name": "created_at", "displayName": "Created", "shape": "date", "role": "timestamp" }, + { "name": "resolved_at", "displayName": "Resolved", "shape": "date" }, + { "name": "parent_id", "displayName": "Parent ID", "shape": "string", "visible": false }, + { + "name": "isReply", + "displayName": "Is Reply", + "computed": true, + "valueExpression": "{{ $['parent_id'] ? true : false }}", + "shape": "boolean" + } + ], + "timeframes": false +} diff --git a/plugins/Figma/v1/dataStreams/fileSummary.json b/plugins/Figma/v1/dataStreams/fileSummary.json new file mode 100644 index 00000000..6d761922 --- /dev/null +++ b/plugins/Figma/v1/dataStreams/fileSummary.json @@ -0,0 +1,58 @@ +{ + "name": "fileSummary", + "displayName": "File Summary", + "description": "Current metadata for a specific Figma file", + "tags": ["Files"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "files/{{fileKey}}/meta", + "pathToData": "file", + "expandInnerObjects": true + }, + "matches": "none", + "ui": [ + { + "name": "project", + "type": "autocomplete", + "label": "Project", + "placeholder": "Select a Figma project", + "validation": { "required": true, "message": "You must select a project" }, + "allowCustomValues": false, + "isMulti": false, + "data": { "source": "dataStream", "dataStreamName": "projectsPicker", "dataSourceConfig": {} } + }, + { + "name": "fileKey", + "type": "autocomplete", + "label": "File", + "placeholder": "Select a file", + "validation": { "required": true, "message": "You must select a file" }, + "allowCustomValues": false, + "isMulti": false, + "data": { "source": "dataStream", "dataStreamName": "filesInProject", "dataSourceConfig": { "endpointPath": { "fieldName": "project", "required": true } } } + } + ], + "metadata": [ + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "folder_name", "displayName": "Folder", "shape": "string" }, + { "name": "last_touched_at", "displayName": "Last Touched", "shape": "date", "role": "timestamp" }, + { "name": "creator.handle", "displayName": "Created By", "shape": "string" }, + { "name": "creator.id", "visible": false }, + { "name": "creator.img_url", "visible": false }, + { "name": "last_touched_by.handle", "displayName": "Last Touched By", "shape": "string" }, + { "name": "last_touched_by.id", "visible": false }, + { "name": "last_touched_by.img_url", "visible": false }, + { "name": "thumbnail_url", "displayName": "Thumbnail", "shape": "url" }, + { "name": "editorType", "displayName": "Editor Type", "shape": "string" }, + { "name": "version", "displayName": "Version", "shape": "string" }, + { "name": "link_access", "displayName": "Link Access", "shape": "string" }, + { "name": "role", "displayName": "Your Role", "shape": "string" }, + { + "name": "url", + "displayName": "Open in Figma", + "shape": ["url", { "label": "Open in Figma" }] + } + ], + "timeframes": false +} diff --git a/plugins/Figma/v1/dataStreams/fileVersions.json b/plugins/Figma/v1/dataStreams/fileVersions.json new file mode 100644 index 00000000..048a19c1 --- /dev/null +++ b/plugins/Figma/v1/dataStreams/fileVersions.json @@ -0,0 +1,58 @@ +{ + "name": "fileVersions", + "displayName": "File Versions", + "description": "Version history for a specific Figma file", + "tags": ["Files", "Version History"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "files/{{fileKey}}/versions", + "pathToData": "versions", + "expandInnerObjects": true, + "paging": { + "mode": "nextUrl", + "in": { "realm": "payload", "path": "pagination.next_page" } + } + }, + "matches": "none", + "ui": [ + { + "name": "project", + "type": "autocomplete", + "label": "Project", + "placeholder": "Select a Figma project", + "validation": { "required": true, "message": "You must select a project" }, + "allowCustomValues": false, + "isMulti": false, + "data": { "source": "dataStream", "dataStreamName": "projectsPicker", "dataSourceConfig": {} } + }, + { + "name": "fileKey", + "type": "autocomplete", + "label": "File", + "placeholder": "Select a file", + "validation": { "required": true, "message": "You must select a file" }, + "allowCustomValues": false, + "isMulti": false, + "data": { "source": "dataStream", "dataStreamName": "filesInProject", "dataSourceConfig": { "endpointPath": { "fieldName": "project", "required": true } } } + } + ], + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "created_at", "displayName": "Created", "shape": "date", "role": "timestamp" }, + { "name": "label", "displayName": "Label", "shape": "string" }, + { "name": "description", "displayName": "Description", "shape": "string" }, + { + "name": "versionName", + "displayName": "Version", + "computed": true, + "valueExpression": "{{ (typeof $['label'] === 'string' && $['label']) || (typeof $['description'] === 'string' && $['description']) || $['created_at'] }}", + "shape": "string", + "role": "label" + }, + { "name": "user.handle", "displayName": "Created By", "shape": "string" }, + { "name": "user.id", "visible": false }, + { "name": "user.img_url", "visible": false } + ], + "timeframes": false +} diff --git a/plugins/Figma/v1/dataStreams/filesInProject.json b/plugins/Figma/v1/dataStreams/filesInProject.json new file mode 100644 index 00000000..b814bc07 --- /dev/null +++ b/plugins/Figma/v1/dataStreams/filesInProject.json @@ -0,0 +1,34 @@ +{ + "name": "filesInProject", + "displayName": "Files in Project", + "description": "Retrieve a list of files within a specified Figma project for use as an autocomplete source", + "tags": ["Files"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "pathToData": "files" + }, + "matches": "none", + "visibility": { + "type": "hidden" + }, + "metadata": [ + { "name": "key", "displayName": "Key", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "visible": false }, + { + "name": "label", + "displayName": "Label", + "computed": true, + "valueExpression": "{{ $['name'] }}", + "role": "label" + }, + { + "name": "value", + "displayName": "Value", + "computed": true, + "valueExpression": "{{ $['key'] }}", + "role": "value" + } + ], + "timeframes": false +} diff --git a/plugins/Figma/v1/dataStreams/projectFiles.json b/plugins/Figma/v1/dataStreams/projectFiles.json new file mode 100644 index 00000000..42e4ce84 --- /dev/null +++ b/plugins/Figma/v1/dataStreams/projectFiles.json @@ -0,0 +1,27 @@ +{ + "name": "projectFiles", + "displayName": "Project Files", + "description": "Files within a Figma project", + "tags": ["Files"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "projects/{{object.rawId}}/files", + "pathToData": "files" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Figma Project"] } }, + "metadata": [ + { "name": "key", "displayName": "Key", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "thumbnail_url", "displayName": "Thumbnail", "shape": "url" }, + { "name": "last_modified", "displayName": "Last Modified", "shape": "date" }, + { + "name": "fileUrl", + "displayName": "Open in Figma", + "computed": true, + "valueExpression": "{{ 'https://www.figma.com/file/' + $['key'] + '/' }}", + "shape": ["url", { "label": "Open in Figma" }] + } + ], + "timeframes": false +} diff --git a/plugins/Figma/v1/dataStreams/projectsPicker.json b/plugins/Figma/v1/dataStreams/projectsPicker.json new file mode 100644 index 00000000..e1150a33 --- /dev/null +++ b/plugins/Figma/v1/dataStreams/projectsPicker.json @@ -0,0 +1,35 @@ +{ + "name": "projectsPicker", + "displayName": "Projects Picker", + "description": "Retrieve a list of projects within the configured Figma team for use as an autocomplete source", + "tags": ["Projects"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "teams/{{dataSource.teamId}}/projects", + "pathToData": "projects" + }, + "matches": "none", + "visibility": { + "type": "hidden" + }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "visible": false }, + { + "name": "label", + "displayName": "Label", + "computed": true, + "valueExpression": "{{ $['name'] }}", + "role": "label" + }, + { + "name": "value", + "displayName": "Value", + "computed": true, + "valueExpression": "{{ 'projects/' + $['id'] + '/files' }}", + "role": "value" + } + ], + "timeframes": false +} diff --git a/plugins/Figma/v1/dataStreams/teamComponents.json b/plugins/Figma/v1/dataStreams/teamComponents.json new file mode 100644 index 00000000..1c15f176 --- /dev/null +++ b/plugins/Figma/v1/dataStreams/teamComponents.json @@ -0,0 +1,38 @@ +{ + "name": "teamComponents", + "displayName": "Team Components", + "description": "Published components in the team's library", + "tags": ["Components", "Design System"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "teams/{{dataSource.teamId}}/components", + "pathToData": "meta.components", + "expandInnerObjects": true, + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, + "in": { "realm": "payload", "path": "meta.cursor.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": "none", + "metadata": [ + { "name": "key", "displayName": "Key", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "description", "displayName": "Description", "shape": "string" }, + { "name": "file_key", "displayName": "File Key", "shape": "string", "visible": false }, + { "name": "created_at", "displayName": "Created", "shape": "date" }, + { "name": "updated_at", "displayName": "Updated", "shape": "date" }, + { "name": "user.handle", "displayName": "Created By", "shape": "string" }, + { + "name": "fileUrl", + "displayName": "Open in Figma", + "computed": true, + "valueExpression": "{{ 'https://www.figma.com/file/' + $['file_key'] + '/' }}", + "shape": ["url", { "label": "Open in Figma" }] + }, + { "sourceId": "key", "sourceType": "Figma Component", "name": "name" } + ], + "timeframes": false +} diff --git a/plugins/Figma/v1/dataStreams/teamProjects.json b/plugins/Figma/v1/dataStreams/teamProjects.json new file mode 100644 index 00000000..7949e95f --- /dev/null +++ b/plugins/Figma/v1/dataStreams/teamProjects.json @@ -0,0 +1,19 @@ +{ + "name": "teamProjects", + "displayName": "Team Projects", + "description": "Projects within the configured Figma team", + "tags": ["Projects"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "teams/{{dataSource.teamId}}/projects", + "pathToData": "projects" + }, + "matches": "none", + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "sourceId": "id", "sourceType": "Figma Project", "name": "name" } + ], + "timeframes": false +} diff --git a/plugins/Figma/v1/dataStreams/teamStyles.json b/plugins/Figma/v1/dataStreams/teamStyles.json new file mode 100644 index 00000000..90483deb --- /dev/null +++ b/plugins/Figma/v1/dataStreams/teamStyles.json @@ -0,0 +1,39 @@ +{ + "name": "teamStyles", + "displayName": "Team Styles", + "description": "Published styles in the team's library", + "tags": ["Styles", "Design System"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "teams/{{dataSource.teamId}}/styles", + "pathToData": "meta.styles", + "expandInnerObjects": true, + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, + "in": { "realm": "payload", "path": "meta.cursor.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": "none", + "metadata": [ + { "name": "key", "displayName": "Key", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "description", "displayName": "Description", "shape": "string" }, + { "name": "style_type", "displayName": "Type", "shape": "string" }, + { "name": "file_key", "displayName": "File Key", "shape": "string", "visible": false }, + { "name": "created_at", "displayName": "Created", "shape": "date" }, + { "name": "updated_at", "displayName": "Updated", "shape": "date" }, + { "name": "user.handle", "displayName": "Created By", "shape": "string" }, + { + "name": "fileUrl", + "displayName": "Open in Figma", + "computed": true, + "valueExpression": "{{ 'https://www.figma.com/file/' + $['file_key'] + '/' }}", + "shape": ["url", { "label": "Open in Figma" }] + }, + { "sourceId": "key", "sourceType": "Figma Style", "name": "name" } + ], + "timeframes": false +} diff --git a/plugins/Figma/v1/defaultContent/componentDashboard.dash.json b/plugins/Figma/v1/defaultContent/componentDashboard.dash.json new file mode 100644 index 00000000..dd017dbc --- /dev/null +++ b/plugins/Figma/v1/defaultContent/componentDashboard.dash.json @@ -0,0 +1,48 @@ +{ + "name": "Component", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "variables": ["{{variables.[Figma Component]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "07993012-9484-46a0-b245-1c19722beb5b", + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "timeframe": "none", + "variables": ["{{variables.[Figma Component]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties" + }, + "scope": { + "scope": "{{scopes.[Figma Components]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Figma Component]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true + } + } + } + } + } + ] + } +} diff --git a/plugins/Figma/v1/defaultContent/fileExplorer.dash.json b/plugins/Figma/v1/defaultContent/fileExplorer.dash.json new file mode 100644 index 00000000..1d0a4cef --- /dev/null +++ b/plugins/Figma/v1/defaultContent/fileExplorer.dash.json @@ -0,0 +1,106 @@ +{ + "name": "File Explorer", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "879f0ba8-5500-40c7-abcf-700f2eeb949b", + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "File Summary", + "description": "Pick a project and a file to see its details", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[fileSummary]}}", + "name": "fileSummary", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true + } + } + } + } + }, + { + "i": "050c42c8-d3f8-4f84-9c03-e41adb4432ff", + "x": 0, + "y": 4, + "w": 2, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Version History", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[fileVersions]}}", + "name": "fileVersions", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["versionName", "created_at", "user.handle"], + "hiddenColumns": ["id", "label", "description", "user.id", "user.img_url"] + } + } + } + } + }, + { + "i": "d90d070c-2800-4229-b237-4272cc3b8187", + "x": 2, + "y": 4, + "w": 2, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Comments", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[fileComments]}}", + "name": "fileComments", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["message", "user.handle", "created_at", "resolved_at", "isReply"], + "hiddenColumns": ["id", "user.id", "user.img_url", "parent_id"] + } + } + } + } + } + ] + } +} diff --git a/plugins/Figma/v1/defaultContent/manifest.json b/plugins/Figma/v1/defaultContent/manifest.json new file mode 100644 index 00000000..d95b5a50 --- /dev/null +++ b/plugins/Figma/v1/defaultContent/manifest.json @@ -0,0 +1,9 @@ +{ + "items": [ + { "name": "overview", "type": "dashboard" }, + { "name": "fileExplorer", "type": "dashboard" }, + { "name": "projectDashboard", "type": "dashboard" }, + { "name": "componentDashboard", "type": "dashboard" }, + { "name": "styleDashboard", "type": "dashboard" } + ] +} diff --git a/plugins/Figma/v1/defaultContent/overview.dash.json b/plugins/Figma/v1/defaultContent/overview.dash.json new file mode 100644 index 00000000..c9d20fb2 --- /dev/null +++ b/plugins/Figma/v1/defaultContent/overview.dash.json @@ -0,0 +1,270 @@ +{ + "name": "Overview", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "b3a78c6e-1eb8-4567-b504-87d92ad81715", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Projects", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[teamProjects]}}", + "name": "teamProjects", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [{ "type": "count" }] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "ac8296f3-7cc4-45cc-b7f6-1fc846aaaa6a", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Components", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[teamComponents]}}", + "name": "teamComponents", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [{ "type": "count" }] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "26e4a4bf-11f7-484c-a40d-0626c9cb9de9", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Styles", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[teamStyles]}}", + "name": "teamStyles", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [{ "type": "count" }] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "3c26727f-e652-4be0-9f84-308573c94e2d", + "x": 0, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Styles by Type", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[teamStyles]}}", + "name": "teamStyles", + "pluginConfigId": "{{configId}}", + "group": { + "by": [["style_type", "uniqueValues"]], + "aggregate": [{ "type": "count" }] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "style_type_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": true, + "legendPosition": "auto", + "legendMode": "table" + } + } + } + } + }, + { + "i": "5d386a9b-3d72-413a-9add-799da0cb0949", + "x": 2, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Top Component Creators", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[teamComponents]}}", + "name": "teamComponents", + "pluginConfigId": "{{configId}}", + "group": { + "by": [["user.handle", "uniqueValues"]], + "aggregate": [{ "type": "count" }] + }, + "sort": { "by": [["count", "desc"]], "top": 10 } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "user.handle_uniqueValues", + "yAxisData": ["count"], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": true, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { "type": "auto" } + } + } + } + } + }, + { + "i": "cd769bf0-def3-4f0d-88dd-fe6630245f1f", + "x": 0, + "y": 6, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Projects", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[teamProjects]}}", + "name": "teamProjects", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["name"] + } + } + } + } + }, + { + "i": "f51ba2cb-24e1-4688-b9ba-da3f9336ad9e", + "x": 0, + "y": 10, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recently Updated Components", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[teamComponents]}}", + "name": "teamComponents", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["updated_at", "desc"]], "top": 10 } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["name", "user.handle", "updated_at", "fileUrl"], + "hiddenColumns": ["key", "file_key", "description", "created_at"] + } + } + } + } + } + ] + } +} diff --git a/plugins/Figma/v1/defaultContent/projectDashboard.dash.json b/plugins/Figma/v1/defaultContent/projectDashboard.dash.json new file mode 100644 index 00000000..9cefbbcc --- /dev/null +++ b/plugins/Figma/v1/defaultContent/projectDashboard.dash.json @@ -0,0 +1,127 @@ +{ + "name": "Project", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "variables": ["{{variables.[Figma Project]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "00184716-8cca-4de2-958f-89b3c5599ce2", + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "timeframe": "none", + "variables": ["{{variables.[Figma Project]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties" + }, + "scope": { + "scope": "{{scopes.[Figma Projects]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Figma Project]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true + } + } + } + } + }, + { + "i": "bcb8c94e-2e0d-4536-8015-df5d494f2566", + "x": 0, + "y": 4, + "w": 1, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Files", + "description": "", + "timeframe": "none", + "variables": ["{{variables.[Figma Project]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[projectFiles]}}", + "name": "projectFiles", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [{ "type": "count" }] + } + }, + "scope": { + "scope": "{{scopes.[Figma Projects]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Figma Project]}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "ca4f55b3-93a9-41e8-8648-045286270c44", + "x": 1, + "y": 4, + "w": 3, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Files in this Project", + "description": "", + "timeframe": "none", + "variables": ["{{variables.[Figma Project]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[projectFiles]}}", + "name": "projectFiles", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Figma Projects]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Figma Project]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["name", "thumbnail_url", "last_modified", "fileUrl"], + "hiddenColumns": ["key"] + } + } + } + } + } + ] + } +} diff --git a/plugins/Figma/v1/defaultContent/scopes.json b/plugins/Figma/v1/defaultContent/scopes.json new file mode 100644 index 00000000..d48ba94c --- /dev/null +++ b/plugins/Figma/v1/defaultContent/scopes.json @@ -0,0 +1,38 @@ +[ + { + "name": "Figma Projects", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Figma Project"] } + }, + "variable": { + "name": "Figma Project", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Figma Components", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Figma Component"] } + }, + "variable": { + "name": "Figma Component", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Figma Styles", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Figma Style"] } + }, + "variable": { + "name": "Figma Style", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + } +] diff --git a/plugins/Figma/v1/defaultContent/styleDashboard.dash.json b/plugins/Figma/v1/defaultContent/styleDashboard.dash.json new file mode 100644 index 00000000..656d3ccb --- /dev/null +++ b/plugins/Figma/v1/defaultContent/styleDashboard.dash.json @@ -0,0 +1,48 @@ +{ + "name": "Style", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "variables": ["{{variables.[Figma Style]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "800db485-3f27-4472-9ea1-2b87e598bc71", + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "timeframe": "none", + "variables": ["{{variables.[Figma Style]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties" + }, + "scope": { + "scope": "{{scopes.[Figma Styles]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Figma Style]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true + } + } + } + } + } + ] + } +} diff --git a/plugins/Figma/v1/docs/README.md b/plugins/Figma/v1/docs/README.md new file mode 100644 index 00000000..2f61c5e6 --- /dev/null +++ b/plugins/Figma/v1/docs/README.md @@ -0,0 +1,54 @@ +# Figma + +Bring your Figma team into SquaredUp. This plugin indexes the projects, files, components and styles in a Figma team, and lets you dashboard file summaries, version history and comments. + +## What gets indexed + +| Object type | Represents | +| --- | --- | +| `Figma Project` | A project within the configured Figma team | +| `Figma File` | A design file within a project | +| `Figma Component` | A published component in the team's library | +| `Figma Style` | A published style (fill, text, effect or grid) in the team's library | + +## Prerequisites + +### 1. Create a personal access token + +1. Sign in to Figma and open **Settings** (click your profile photo, top-left). +2. Go to the **Security** tab. +3. Under **Personal access tokens**, click **Generate new token**. +4. Give the token a name and select the following scopes: + - `projects:read` + - `file_metadata:read` + - `file_versions:read` + - `file_comments:read` + - `team_library_content:read` +5. Copy the generated token — Figma only shows it once. + +### 2. Find your Team ID + +Open the team in Figma and copy the numeric ID from the URL: + +``` +https://www.figma.com/files/team/1234567890123456789/My-Team + ^^^^^^^^^^^^^^^^^^^ + This is your Team ID +``` + +> Figma's API has no way to list the teams you belong to — the Team ID must be entered manually. To monitor more than one team, add this plugin again with a different Team ID. + +## Configuration fields + +| Field | Description | Required | +| --- | --- | --- | +| Personal Access Token | The token generated above. Used to authenticate every request to the Figma API. | Yes | +| Team ID | The numeric ID of the Figma team to monitor. | Yes | + +## Known limitations + +- **No team discovery.** Figma's REST API does not expose an endpoint to list a user's teams, so the Team ID must be found and entered manually (see above). +- **Rate limits.** Figma enforces per-token rate limits that vary by plan and endpoint (10–150 requests/minute). This plugin only calls lightweight, scoped endpoints, but very large teams with many files may see slower or throttled imports. +- **Personal access tokens are tied to a Figma user account.** The plugin can only see teams, projects and files that the token's owner has access to. If that person loses access or leaves the team, the plugin will start failing. +- **Full design content isn't imported.** This plugin surfaces file metadata (name, thumbnail, last modified, versions, comments) and library assets (components, styles) — it does not pull the full design document tree. +- **Activity logs and Variables are not supported.** Both require an Enterprise plan and org-admin OAuth scopes that aren't available to a personal access token. diff --git a/plugins/Figma/v1/icon.svg b/plugins/Figma/v1/icon.svg new file mode 100644 index 00000000..30e3e540 --- /dev/null +++ b/plugins/Figma/v1/icon.svg @@ -0,0 +1,13 @@ + + + + + Figma.logo + Created using Figma + + + + + + + diff --git a/plugins/Figma/v1/indexDefinitions/default.json b/plugins/Figma/v1/indexDefinitions/default.json new file mode 100644 index 00000000..97250648 --- /dev/null +++ b/plugins/Figma/v1/indexDefinitions/default.json @@ -0,0 +1,49 @@ +{ + "steps": [ + { + "name": "projects", + "dataStream": { "name": "teamProjects" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Figma Project" } + } + }, + { + "name": "components", + "dataStream": { "name": "teamComponents" }, + "timeframe": "none", + "objectMapping": { + "id": "key", + "name": "name", + "type": { "value": "Figma Component" }, + "properties": [ + "description", + "file_key", + "created_at", + "updated_at", + { "createdBy": "user.handle" } + ] + } + }, + { + "name": "styles", + "dataStream": { "name": "teamStyles" }, + "timeframe": "none", + "objectMapping": { + "id": "key", + "name": "name", + "type": { "value": "Figma Style" }, + "properties": [ + "description", + "style_type", + "file_key", + "created_at", + "updated_at", + { "createdBy": "user.handle" } + ] + } + } + ] +} diff --git a/plugins/Figma/v1/metadata.json b/plugins/Figma/v1/metadata.json new file mode 100644 index 00000000..786e283d --- /dev/null +++ b/plugins/Figma/v1/metadata.json @@ -0,0 +1,52 @@ +{ + "name": "figma", + "displayName": "Figma", + "version": "1.1.0", + "author": { + "name": "@conor-richey-squaredup", + "type": "community" + }, + "description": "Bring your Figma team into SquaredUp — index projects, files, components and styles, and surface file summaries, version history and comments.", + "category": "Collaboration", + "type": "hybrid", + "schemaVersion": "2.1", + "importNotSupported": false, + "restrictedToPlatforms": [], + "keywords": [ + "figma", + "design", + "prototyping", + "ui design", + "collaboration", + "design system" + ], + "objectTypes": [ + "Figma Project", + "Figma Component", + "Figma Style" + ], + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/Figma/v1/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/Figma/v1", + "label": "Repository" + } + ], + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "baseUrl": "https://api.figma.com/v1", + "authMode": "none", + "headers": [ + { "key": "X-Figma-Token", "value": "{{personalAccessToken}}" } + ], + "queryArgs": [] + } + } +} diff --git a/plugins/Figma/v1/ui.json b/plugins/Figma/v1/ui.json new file mode 100644 index 00000000..3f311944 --- /dev/null +++ b/plugins/Figma/v1/ui.json @@ -0,0 +1,25 @@ +[ + { + "type": "password", + "name": "personalAccessToken", + "label": "Personal Access Token", + "help": "A Figma personal access token. Generate one from your Figma account settings under **Settings > Security > Personal access tokens**. Requires at least the `projects:read`, `file_metadata:read`, `file_versions:read`, `file_comments:read` and `team_library_content:read` scopes.", + "validation": { + "required": true + } + }, + { + "type": "text", + "name": "teamId", + "label": "Team ID", + "placeholder": "e.g. 123456789012345678", + "help": "The numeric ID of the Figma team to monitor. Found in the team's URL: `figma.com/files/team//...`. To monitor more than one team, add this plugin again with a different Team ID.", + "validation": { + "required": true, + "pattern": { + "value": "^[0-9]+$", + "message": "Team ID must be numeric" + } + } + } +]