From 587fe5cf2aa69e59187e773475ec5127170c98f0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 3 Mar 2026 11:44:53 +0000
Subject: [PATCH 1/4] Initial plan
From 239b3044af6bfea0f07a2916cd1ba8a58c74a339 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 3 Mar 2026 11:50:34 +0000
Subject: [PATCH 2/4] move figma-plugin into src/figma-plugin, flatten nested
src, update all path references
Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
---
.github/workflows/ci.yml | 2 +-
README.md | 2 +-
eslint.config.mjs | 7 ++++++-
package.json | 2 +-
{figma-plugin/src => src/figma-plugin}/code.ts | 2 +-
.../src => src/figma-plugin}/constants.ts | 0
{figma-plugin/src => src/figma-plugin}/index.html | 0
{figma-plugin/src => src/figma-plugin}/main.css | 0
{figma-plugin/src => src/figma-plugin}/main.tsx | 14 +++++++-------
{figma-plugin => src/figma-plugin}/manifest.json | 0
{figma-plugin => src/figma-plugin}/tsup.config.ts | 4 ++--
{figma-plugin => src/figma-plugin}/vite.config.ts | 4 ++--
tsconfig.json | 2 +-
13 files changed, 22 insertions(+), 17 deletions(-)
rename {figma-plugin/src => src/figma-plugin}/code.ts (98%)
rename {figma-plugin/src => src/figma-plugin}/constants.ts (100%)
rename {figma-plugin/src => src/figma-plugin}/index.html (100%)
rename {figma-plugin/src => src/figma-plugin}/main.css (100%)
rename {figma-plugin/src => src/figma-plugin}/main.tsx (83%)
rename {figma-plugin => src/figma-plugin}/manifest.json (100%)
rename {figma-plugin => src/figma-plugin}/tsup.config.ts (69%)
rename {figma-plugin => src/figma-plugin}/vite.config.ts (84%)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cf9ef6b..caa1c73 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -95,7 +95,7 @@ jobs:
set -euo pipefail
VERSION="${{ needs.ci.outputs.version }}"
- PLUGIN_DIR="figma-plugin"
+ PLUGIN_DIR="src/figma-plugin"
echo "Publishing Figma plugin v${VERSION} (plugin id: ${FIGMA_PLUGIN_ID})"
diff --git a/README.md b/README.md
index 7751c85..8141492 100644
--- a/README.md
+++ b/README.md
@@ -455,7 +455,7 @@ $ pnpm i
1. `pnpm run build-figma`
2. In Figma: Plugins → Development → Import plugin from manifest…
-3. Select `figma-plugin/manifest.json`
+3. Select `src/figma-plugin/manifest.json`
## Validation
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 73d3f65..694cf56 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -8,7 +8,12 @@ const SOURCE_FILES = ["src/**/*.{ts,tsx}", ".storybook/**/*.{ts,tsx}"];
export default defineConfig([
{
- ignores: ["dist/**", "storybook-static/**", "node_modules/**"],
+ ignores: [
+ "dist/**",
+ "storybook-static/**",
+ "node_modules/**",
+ "src/figma-plugin/**",
+ ],
},
{
...reactHooks.configs.flat.recommended,
diff --git a/package.json b/package.json
index 2560f3e..f82ffad 100644
--- a/package.json
+++ b/package.json
@@ -105,7 +105,7 @@
"local-release": "pnpm run lgtm && changeset version && changeset publish",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
- "build-figma": "vite build --config figma-plugin/vite.config.ts && tsup --config figma-plugin/tsup.config.ts",
+ "build-figma": "vite build --config src/figma-plugin/vite.config.ts && tsup --config src/figma-plugin/tsup.config.ts",
"chromatic": "chromatic --project-token $CHROMATIC_PROJECT_TOKEN",
"prepare": "pnpm run pretest && husky",
"changeset": "pnpm exec changeset"
diff --git a/figma-plugin/src/code.ts b/src/figma-plugin/code.ts
similarity index 98%
rename from figma-plugin/src/code.ts
rename to src/figma-plugin/code.ts
index 53d42f5..03c1294 100644
--- a/figma-plugin/src/code.ts
+++ b/src/figma-plugin/code.ts
@@ -1,6 +1,6 @@
///
-import type { FigmaVariable } from "../../src/lib/builder";
+import type { FigmaVariable } from "../lib/builder";
import { COLLECTION_NAME } from "./constants";
// ─── Plugin entry point ─────────────────────────────────────────────────
diff --git a/figma-plugin/src/constants.ts b/src/figma-plugin/constants.ts
similarity index 100%
rename from figma-plugin/src/constants.ts
rename to src/figma-plugin/constants.ts
diff --git a/figma-plugin/src/index.html b/src/figma-plugin/index.html
similarity index 100%
rename from figma-plugin/src/index.html
rename to src/figma-plugin/index.html
diff --git a/figma-plugin/src/main.css b/src/figma-plugin/main.css
similarity index 100%
rename from figma-plugin/src/main.css
rename to src/figma-plugin/main.css
diff --git a/figma-plugin/src/main.tsx b/src/figma-plugin/main.tsx
similarity index 83%
rename from figma-plugin/src/main.tsx
rename to src/figma-plugin/main.tsx
index 22fbeaf..45500b2 100644
--- a/figma-plugin/src/main.tsx
+++ b/src/figma-plugin/main.tsx
@@ -1,13 +1,13 @@
import { useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
-import { Fab } from "../../src/components/m3/Fab";
-import { FlowfieldSt } from "../../src/Flowfield.stories";
-import { FlowfieldScene } from "../../src/Flowfield.stories.helpers";
-import { Mcu } from "../../src/Mcu";
-import { useMcu } from "../../src/Mcu.context";
+import { Fab } from "../components/m3/Fab";
+import { FlowfieldSt } from "../Flowfield.stories";
+import { FlowfieldScene } from "../Flowfield.stories.helpers";
+import { Mcu } from "../Mcu";
+import { useMcu } from "../Mcu.context";
-import { TooltipProvider } from "../../src/components/ui/tooltip";
-import "../../src/styles/globals.css";
+import { TooltipProvider } from "../components/ui/tooltip";
+import "../styles/globals.css";
import "./main.css";
function SyncButton() {
diff --git a/figma-plugin/manifest.json b/src/figma-plugin/manifest.json
similarity index 100%
rename from figma-plugin/manifest.json
rename to src/figma-plugin/manifest.json
diff --git a/figma-plugin/tsup.config.ts b/src/figma-plugin/tsup.config.ts
similarity index 69%
rename from figma-plugin/tsup.config.ts
rename to src/figma-plugin/tsup.config.ts
index b8673a1..3f37c4e 100644
--- a/figma-plugin/tsup.config.ts
+++ b/src/figma-plugin/tsup.config.ts
@@ -1,11 +1,11 @@
import { defineConfig } from "tsup";
export default defineConfig({
- entryPoints: ["figma-plugin/src/code.ts"],
+ entryPoints: ["src/figma-plugin/code.ts"],
format: ["iife"],
target: "es2015",
dts: false,
- outDir: "figma-plugin/dist",
+ outDir: "src/figma-plugin/dist",
clean: false,
outExtension: () => ({ js: ".js" }),
});
diff --git a/figma-plugin/vite.config.ts b/src/figma-plugin/vite.config.ts
similarity index 84%
rename from figma-plugin/vite.config.ts
rename to src/figma-plugin/vite.config.ts
index 960fcf4..a166326 100644
--- a/figma-plugin/vite.config.ts
+++ b/src/figma-plugin/vite.config.ts
@@ -7,11 +7,11 @@ import tsconfigPaths from "vite-tsconfig-paths";
const dir = import.meta.dirname;
export default defineConfig({
- root: resolve(dir, "src"),
+ root: dir,
plugins: [
react(),
viteSingleFile(),
- tsconfigPaths({ root: resolve(dir, "..") }),
+ tsconfigPaths({ root: resolve(dir, "../..") }),
],
build: {
outDir: resolve(dir, "dist"),
diff --git a/tsconfig.json b/tsconfig.json
index 80ee5af..6baaca6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -26,5 +26,5 @@
"noEmit": true
},
"include": ["src/**/*"],
- "exclude": ["node_modules", "dist"]
+ "exclude": ["node_modules", "dist", "src/figma-plugin"]
}
From 437ebe04d53187715434275af0dfc1ee1fdcd520 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 3 Mar 2026 13:04:28 +0000
Subject: [PATCH 3/4] remove config exclusions, fix pre-existing lint/typecheck
issues in figma-plugin code instead
Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
---
eslint.config.mjs | 7 +----
src/figma-plugin/code.ts | 54 +++++++++++++++++++++++----------------
src/figma-plugin/main.tsx | 1 +
tsconfig.json | 2 +-
4 files changed, 35 insertions(+), 29 deletions(-)
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 694cf56..73d3f65 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -8,12 +8,7 @@ const SOURCE_FILES = ["src/**/*.{ts,tsx}", ".storybook/**/*.{ts,tsx}"];
export default defineConfig([
{
- ignores: [
- "dist/**",
- "storybook-static/**",
- "node_modules/**",
- "src/figma-plugin/**",
- ],
+ ignores: ["dist/**", "storybook-static/**", "node_modules/**"],
},
{
...reactHooks.configs.flat.recommended,
diff --git a/src/figma-plugin/code.ts b/src/figma-plugin/code.ts
index 03c1294..b77d78b 100644
--- a/src/figma-plugin/code.ts
+++ b/src/figma-plugin/code.ts
@@ -1,6 +1,6 @@
///
-import type { FigmaVariable } from "../lib/builder";
+import type { FigmaVariable, FigmaVariableValue } from "../lib/builder";
import { COLLECTION_NAME } from "./constants";
// ─── Plugin entry point ─────────────────────────────────────────────────
@@ -37,14 +37,16 @@ async function findOrCreateCollection(name: string) {
function ensureModes(collection: VariableCollection, ...modeNames: string[]) {
const result: Record = {};
for (let i = 0; i < modeNames.length; i++) {
- const existing = collection.modes.find((m) => m.name === modeNames[i]);
+ const name = modeNames[i] as string;
+ const existing = collection.modes.find((m) => m.name === name);
if (existing) {
- result[modeNames[i]] = existing.modeId;
+ result[name] = existing.modeId;
} else if (i === 0 && collection.modes.length === 1) {
- collection.renameMode(collection.modes[0].modeId, modeNames[i]);
- result[modeNames[i]] = collection.modes[0].modeId;
+ const first = collection.modes[0] as { modeId: string; name: string };
+ collection.renameMode(first.modeId, name);
+ result[name] = first.modeId;
} else {
- result[modeNames[i]] = collection.addMode(modeNames[i]);
+ result[name] = collection.addMode(name);
}
}
return result;
@@ -64,6 +66,28 @@ async function findOrCreateVariable(
// ─── Sync logic ─────────────────────────────────────────────────────────
+function setModeValue(
+ variable: Variable,
+ modeId: string,
+ value: FigmaVariableValue,
+ varMap: Record,
+) {
+ if ("alias" in value) {
+ const target = varMap[value.alias];
+ if (target) {
+ variable.setValueForMode(
+ modeId,
+ figma.variables.createVariableAlias(target),
+ );
+ } else {
+ console.warn(`Alias target not found: ${value.alias}`);
+ variable.setValueForMode(modeId, { r: 0, g: 0, b: 0, a: 1 });
+ }
+ } else {
+ variable.setValueForMode(modeId, value);
+ }
+}
+
async function syncVariables(variables: FigmaVariable[]) {
const collection = await findOrCreateCollection(COLLECTION_NAME);
const modes = ensureModes(collection, "Light", "Dark");
@@ -77,28 +101,14 @@ async function syncVariables(variables: FigmaVariable[]) {
// Set values and metadata
for (const v of variables) {
const variable = varMap[v.path];
+ if (!variable) continue;
if (v.description) variable.description = v.description;
if (v.scopes) variable.scopes = v.scopes as VariableScope[];
for (const [modeName, modeId] of Object.entries(modes)) {
const value = v.values[modeName];
- if (!value) continue;
-
- if ("alias" in value) {
- const target = varMap[value.alias];
- if (target) {
- variable.setValueForMode(
- modeId,
- figma.variables.createVariableAlias(target),
- );
- } else {
- console.warn(`Alias target not found: ${value.alias}`);
- variable.setValueForMode(modeId, { r: 0, g: 0, b: 0, a: 1 });
- }
- } else {
- variable.setValueForMode(modeId, value);
- }
+ if (value) setModeValue(variable, modeId, value, varMap);
}
}
diff --git a/src/figma-plugin/main.tsx b/src/figma-plugin/main.tsx
index 45500b2..4b709f8 100644
--- a/src/figma-plugin/main.tsx
+++ b/src/figma-plugin/main.tsx
@@ -73,4 +73,5 @@ function App() {
);
}
+// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
createRoot(document.getElementById("root")!).render();
diff --git a/tsconfig.json b/tsconfig.json
index 6baaca6..80ee5af 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -26,5 +26,5 @@
"noEmit": true
},
"include": ["src/**/*"],
- "exclude": ["node_modules", "dist", "src/figma-plugin"]
+ "exclude": ["node_modules", "dist"]
}
From ca714d99c83925de3b29774d2dff27abcc91f0a3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 3 Mar 2026 13:06:25 +0000
Subject: [PATCH 4/4] replace type assertions with proper null guards per code
review
Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
---
src/figma-plugin/code.ts | 6 ++++--
src/figma-plugin/main.tsx | 4 ++--
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/figma-plugin/code.ts b/src/figma-plugin/code.ts
index b77d78b..9f9aa0e 100644
--- a/src/figma-plugin/code.ts
+++ b/src/figma-plugin/code.ts
@@ -37,12 +37,14 @@ async function findOrCreateCollection(name: string) {
function ensureModes(collection: VariableCollection, ...modeNames: string[]) {
const result: Record = {};
for (let i = 0; i < modeNames.length; i++) {
- const name = modeNames[i] as string;
+ const name = modeNames[i];
+ if (!name) continue;
const existing = collection.modes.find((m) => m.name === name);
if (existing) {
result[name] = existing.modeId;
} else if (i === 0 && collection.modes.length === 1) {
- const first = collection.modes[0] as { modeId: string; name: string };
+ const first = collection.modes[0];
+ if (!first) continue;
collection.renameMode(first.modeId, name);
result[name] = first.modeId;
} else {
diff --git a/src/figma-plugin/main.tsx b/src/figma-plugin/main.tsx
index 4b709f8..f695310 100644
--- a/src/figma-plugin/main.tsx
+++ b/src/figma-plugin/main.tsx
@@ -73,5 +73,5 @@ function App() {
);
}
-// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-createRoot(document.getElementById("root")!).render();
+const root = document.getElementById("root");
+if (root) createRoot(root).render();