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 @@
+
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"
+ }
+ }
+ }
+]