inkbridge 0.1.0-beta.5 → 0.1.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ <img src="https://inkbridge.ink/inkbridge-logo.png" alt="Inkbridge" width="80" />
2
+
1
3
  # Inkbridge
2
4
 
3
5
  Generates native Figma frames from your Tailwind React components and syncs design tokens back to your codebase via GitHub PRs.
package/code.js CHANGED
@@ -26,7 +26,7 @@
26
26
  repo: "",
27
27
  baseBranch: "main",
28
28
  tokenPath: "design-tokens/tokens.dtcg.json",
29
- tokenSourceMode: "auto",
29
+ tokenSourceMode: "css",
30
30
  cssTokenPath: "",
31
31
  syncDtcgOnPush: false,
32
32
  allowNewTokensFromFigma: false,
@@ -44,8 +44,8 @@
44
44
  return tokenPath;
45
45
  }
46
46
  function normalizeTokenSourceMode(mode) {
47
- if (mode === "css" || mode === "dtcg" || mode === "auto") return mode;
48
- return "auto";
47
+ if (mode === "dtcg") return "dtcg";
48
+ return "css";
49
49
  }
50
50
  function normalizeCssTokenPath(cssTokenPath) {
51
51
  if (typeof cssTokenPath !== "string") return "";
@@ -1696,7 +1696,7 @@
1696
1696
  function normalizeTokenMap(raw) {
1697
1697
  if (!raw || !isPlainObject3(raw)) return void 0;
1698
1698
  const mode = raw.mode === "css" || raw.mode === "dtcg" || raw.mode === "embedded" ? raw.mode : "embedded";
1699
- const requestedMode = raw.requestedMode === "css" || raw.requestedMode === "dtcg" || raw.requestedMode === "auto" ? raw.requestedMode : void 0;
1699
+ const requestedMode = raw.requestedMode === "css" || raw.requestedMode === "dtcg" ? raw.requestedMode : void 0;
1700
1700
  const out = createEmptyScannedTokenMap(
1701
1701
  mode,
1702
1702
  typeof raw.source === "string" && raw.source ? raw.source : "embedded:tokens.ts",
@@ -1803,7 +1803,7 @@
1803
1803
  const baseUrl = "http://localhost:" + port + "/" + path;
1804
1804
  const query = [];
1805
1805
  const requestedMode = config == null ? void 0 : config.tokenSourceMode;
1806
- if (requestedMode === "auto" || requestedMode === "css" || requestedMode === "dtcg") {
1806
+ if (requestedMode === "css" || requestedMode === "dtcg") {
1807
1807
  query.push("tokenSourceMode=" + encodeURIComponent(requestedMode));
1808
1808
  }
1809
1809
  const cssTokenPath = ((config == null ? void 0 : config.cssTokenPath) || "").trim();
@@ -4402,7 +4402,7 @@
4402
4402
  const modeRaw = raw && typeof raw === "object" ? raw.mode : null;
4403
4403
  const mode = modeRaw === "css" || modeRaw === "dtcg" || modeRaw === "embedded" ? modeRaw : "embedded";
4404
4404
  const requestedRaw = raw && typeof raw === "object" ? raw.requestedMode : null;
4405
- const requestedMode = requestedRaw === "auto" || requestedRaw === "css" || requestedRaw === "dtcg" ? requestedRaw : void 0;
4405
+ const requestedMode = requestedRaw === "css" || requestedRaw === "dtcg" ? requestedRaw : void 0;
4406
4406
  return { source, mode, requestedMode };
4407
4407
  }
4408
4408
  async function loadTokenSourceInfo() {
@@ -4823,8 +4823,8 @@
4823
4823
  return true;
4824
4824
  }
4825
4825
  function resolveConfiguredMode(mode) {
4826
- if (mode === "css" || mode === "dtcg" || mode === "auto") return mode;
4827
- return "auto";
4826
+ if (mode === "dtcg") return "dtcg";
4827
+ return "css";
4828
4828
  }
4829
4829
  async function buildTokenCommitPlan(token) {
4830
4830
  const tokensBeforeVariablePatch = JSON.parse(JSON.stringify(TOKENS));
@@ -4838,7 +4838,7 @@
4838
4838
  const configuredMode = resolveConfiguredMode(GITHUB_CONFIG.tokenSourceMode);
4839
4839
  const dtcgPath = normalizePath(GITHUB_CONFIG.tokenPath, DEFAULT_DTCG_PATH);
4840
4840
  const dtcgExisting = await fetchFileContent(token, dtcgPath, GITHUB_CONFIG.baseBranch);
4841
- const preferDtcg = configuredMode === "dtcg" || configuredMode === "auto" && sourceInfo.mode === "dtcg";
4841
+ const preferDtcg = configuredMode === "dtcg" || configuredMode === "css" && sourceInfo.mode === "dtcg";
4842
4842
  if (preferDtcg) {
4843
4843
  const dtcg = tokensToDTCG(workingTokens);
4844
4844
  const dtcgContent = JSON.stringify(dtcg, null, 2) + "\n";
@@ -11760,7 +11760,7 @@
11760
11760
  }
11761
11761
  function coerceTokenSourceInfo(raw) {
11762
11762
  const mode = raw && (raw.mode === "css" || raw.mode === "dtcg" || raw.mode === "embedded") ? raw.mode : "embedded";
11763
- const requestedMode = raw && (raw.requestedMode === "auto" || raw.requestedMode === "css" || raw.requestedMode === "dtcg") ? raw.requestedMode : void 0;
11763
+ const requestedMode = raw && (raw.requestedMode === "css" || raw.requestedMode === "dtcg") ? raw.requestedMode : void 0;
11764
11764
  const source = raw && typeof raw.source === "string" && raw.source.trim() ? raw.source.trim() : "embedded:tokens.ts";
11765
11765
  return { source, mode, requestedMode };
11766
11766
  }
@@ -12101,7 +12101,7 @@
12101
12101
  repo: msg.repo || "",
12102
12102
  baseBranch: msg.baseBranch || "main",
12103
12103
  tokenPath: msg.tokenPath || "design-tokens/tokens.dtcg.json",
12104
- tokenSourceMode: msg.tokenSourceMode || "auto",
12104
+ tokenSourceMode: msg.tokenSourceMode || "css",
12105
12105
  cssTokenPath: msg.cssTokenPath || "",
12106
12106
  syncDtcgOnPush: msg.syncDtcgOnPush === true,
12107
12107
  allowNewTokensFromFigma: msg.allowNewTokensFromFigma === true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inkbridge",
3
- "version": "0.1.0-beta.5",
3
+ "version": "0.1.0-beta.7",
4
4
  "description": "Figma plugin that generates a pixel-accurate design system from your Tailwind React components.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,10 +42,5 @@
42
42
  "storybook"
43
43
  ],
44
44
  "license": "MIT",
45
- "homepage": "https://inkbridge.io",
46
- "repository": {
47
- "type": "git",
48
- "url": "https://github.com/inkn9ne/inkbridge.git",
49
- "directory": "tools/figma-plugin-tailwind-tokens"
50
- }
45
+ "homepage": "https://inkbridge.ink"
51
46
  }
@@ -84,6 +84,10 @@ function resolveImportedCssPath(baseFilePath: string, params: string): string |
84
84
  }
85
85
 
86
86
  function cssHasTokenDeclarations(cssText: string): boolean {
87
+ // Only count declarations inside :root {} or .[theme] {} rules — the same
88
+ // selectors that patchCssVariables targets. We intentionally skip @theme
89
+ // at-rules (Tailwind v4 utility mappings) because those contain var()
90
+ // references, not source values, and the patcher cannot update them.
87
91
  try {
88
92
  const root = postcss.parse(cssText);
89
93
  let found = false;
@@ -91,16 +95,9 @@ function cssHasTokenDeclarations(cssText: string): boolean {
91
95
  if (found) return;
92
96
  if (!decl.prop || !decl.prop.startsWith('--')) return;
93
97
  const parent = decl.parent;
94
- if (!parent) return;
95
- if (parent.type === 'atrule') {
96
- const at = parent as AtRule;
97
- if ((at.name || '').toLowerCase() === 'theme') found = true;
98
- return;
99
- }
100
- if (parent.type === 'rule') {
101
- const selector = (parent as Rule).selector || '';
102
- if (parseThemeSelectors(selector).length > 0) found = true;
103
- }
98
+ if (!parent || parent.type !== 'rule') return;
99
+ const selector = (parent as Rule).selector || '';
100
+ if (parseThemeSelectors(selector).length > 0) found = true;
104
101
  });
105
102
  return found;
106
103
  } catch {
@@ -108,7 +105,7 @@ function cssHasTokenDeclarations(cssText: string): boolean {
108
105
  }
109
106
  }
110
107
 
111
- function resolveCssTokenPathFromImports(filePath: string, visited: Set<string> = new Set()): string {
108
+ export function resolveCssTokenPathFromImports(filePath: string, visited: Set<string> = new Set()): string {
112
109
  const absolute = path.resolve(filePath);
113
110
  if (visited.has(absolute)) return absolute;
114
111
  visited.add(absolute);
@@ -383,7 +380,7 @@ function walkCssNodes(map: ScannedTokenMap, container: postcss.Container): void
383
380
  }
384
381
  }
385
382
 
386
- export function parseCssTokenMap(cssText: string, source: string, requestedMode: TokenSourceMode = 'auto'): ScannedTokenMap {
383
+ export function parseCssTokenMap(cssText: string, source: string, requestedMode: TokenSourceMode = 'css'): ScannedTokenMap {
387
384
  const map = createEmptyScannedTokenMap('css', source, requestedMode);
388
385
  const root = postcss.parse(cssText);
389
386
  walkCssNodes(map, root);
@@ -424,7 +421,7 @@ function applyDtcgDimensionGroup(
424
421
  function parseDtcgTokenMap(
425
422
  dtcgJson: unknown,
426
423
  source: string,
427
- requestedMode: TokenSourceMode = 'auto'
424
+ requestedMode: TokenSourceMode = 'dtcg'
428
425
  ): ScannedTokenMap {
429
426
  const map = createEmptyScannedTokenMap('dtcg', source, requestedMode);
430
427
  if (!isPlainObject(dtcgJson)) return map;
@@ -452,16 +449,10 @@ function parseDtcgTokenMap(
452
449
 
453
450
  export function readTokenSourceMap(options: ReadTokenSourceOptions): ScannedTokenMap {
454
451
  const projectRoot = path.resolve(options.projectRoot);
455
- const requestedMode = options.tokenSourceMode || 'auto';
452
+ const requestedMode: TokenSourceMode = options.tokenSourceMode === 'dtcg' ? 'dtcg' : 'css';
456
453
  const cssPath = discoverCssTokenPath(projectRoot, options.cssTokenPath);
457
454
  const dtcgPath = discoverDtcgTokenPath(projectRoot, options.dtcgTokenPath);
458
455
 
459
- if (requestedMode === 'css') {
460
- if (!cssPath) return createEmptyScannedTokenMap('embedded', 'embedded:tokens.ts', requestedMode);
461
- const cssText = readCssWithImports(cssPath);
462
- return parseCssTokenMap(cssText, toDisplayPath(projectRoot, cssPath), requestedMode);
463
- }
464
-
465
456
  if (requestedMode === 'dtcg') {
466
457
  if (!dtcgPath) return createEmptyScannedTokenMap('embedded', 'embedded:tokens.ts', requestedMode);
467
458
  const dtcgText = fs.readFileSync(dtcgPath, 'utf-8');
@@ -469,10 +460,14 @@ export function readTokenSourceMap(options: ReadTokenSourceOptions): ScannedToke
469
460
  return parseDtcgTokenMap(dtcgJson, toDisplayPath(projectRoot, dtcgPath), requestedMode);
470
461
  }
471
462
 
472
- // auto mode: CSS always wins when present.
463
+ // css mode: CSS preferred; fall back to DTCG if no CSS file found, then embedded.
473
464
  if (cssPath) {
474
465
  const cssText = readCssWithImports(cssPath);
475
- return parseCssTokenMap(cssText, toDisplayPath(projectRoot, cssPath), requestedMode);
466
+ // Use the file that actually contains the declarations as source so the
467
+ // write-back path (PR/patch) targets the right file even when globals.css
468
+ // delegates token definitions to an imported file like tokens.css.
469
+ const writeTarget = resolveCssTokenPathFromImports(cssPath);
470
+ return parseCssTokenMap(cssText, toDisplayPath(projectRoot, writeTarget), requestedMode);
476
471
  }
477
472
  if (dtcgPath) {
478
473
  const dtcgText = fs.readFileSync(dtcgPath, 'utf-8');
@@ -1,4 +1,4 @@
1
- export type TokenSourceMode = 'auto' | 'css' | 'dtcg';
1
+ export type TokenSourceMode = 'css' | 'dtcg';
2
2
  export type ResolvedTokenSourceMode = 'css' | 'dtcg' | 'embedded';
3
3
 
4
4
  export interface ScannedThemeTokens {
package/ui.html CHANGED
@@ -205,7 +205,7 @@
205
205
  </div>
206
206
  <div id="tokenSourceInfoPush" class="repo-display" style="display:none;padding:6px 8px;">
207
207
  Last scan source: <strong id="tokenSourceLabelPush"></strong><br>
208
- Configured mode: <strong id="configuredTokenSourceModePush">auto</strong>
208
+ Configured mode: <strong id="configuredTokenSourceModePush">css</strong>
209
209
  </div>
210
210
 
211
211
  <div class="field">
@@ -250,7 +250,7 @@
250
250
 
251
251
  <div id="tokenSourceInfoSettings" class="repo-display" style="display:none;padding:6px 8px;">
252
252
  Last scan source: <strong id="tokenSourceLabelSettings"></strong><br>
253
- Configured mode: <strong id="configuredTokenSourceModeSettings">auto</strong>
253
+ Configured mode: <strong id="configuredTokenSourceModeSettings">css</strong>
254
254
  </div>
255
255
 
256
256
  <div class="field">
@@ -271,11 +271,10 @@
271
271
  <div class="field">
272
272
  <label>Token Source Mode</label>
273
273
  <select id="settingsTokenSourceMode">
274
- <option value="auto">auto (prefer CSS)</option>
275
- <option value="css">css (force CSS only)</option>
276
- <option value="dtcg">dtcg (force DTCG only)</option>
274
+ <option value="css">css (auto-discover CSS, fallback to DTCG)</option>
275
+ <option value="dtcg">dtcg (force DTCG file only)</option>
277
276
  </select>
278
- <p class="hint">`auto` prefers CSS and falls back to DTCG, then embedded tokens.</p>
277
+ <p class="hint">CSS mode auto-discovers your globals.css and falls back to DTCG if not found. Use DTCG to force the legacy token file.</p>
279
278
  </div>
280
279
 
281
280
  <div class="field" id="tokenPathField">
@@ -291,25 +290,25 @@
291
290
  </div>
292
291
 
293
292
  <div class="field" id="syncDtcgOnPushField">
294
- <label style="display:flex;align-items:center;gap:8px;">
295
- <input type="checkbox" id="settingsSyncDtcgOnPush">
296
- Also update DTCG on Push to Code
297
- </label>
293
+ <div style="display:flex;align-items:center;gap:8px;">
294
+ <input type="checkbox" id="settingsSyncDtcgOnPush" style="width:auto;flex-shrink:0;">
295
+ <label for="settingsSyncDtcgOnPush" style="display:inline;margin:0;cursor:pointer;">Also update DTCG on Push to Code</label>
296
+ </div>
298
297
  <p class="hint">When enabled, the plugin also commits <code>tokens.dtcg.json</code> as a generated artifact.</p>
299
298
  </div>
300
299
 
301
300
  <div class="field">
302
- <label style="display:flex;align-items:center;gap:8px;">
303
- <input type="checkbox" id="settingsAllowNewTokensFromFigma">
304
- Allow New Tokens from Figma
305
- </label>
301
+ <div style="display:flex;align-items:center;gap:8px;">
302
+ <input type="checkbox" id="settingsAllowNewTokensFromFigma" style="width:auto;flex-shrink:0;">
303
+ <label for="settingsAllowNewTokensFromFigma" style="display:inline;margin:0;cursor:pointer;">Allow New Tokens from Figma</label>
304
+ </div>
306
305
  <p class="hint">Disabled by default. When enabled, new token keys can be added to code on push.</p>
307
306
  </div>
308
307
 
309
- <div class="field">
308
+ <div class="field" id="newTokenPrefixesField" style="display:none;">
310
309
  <label>New Token Prefixes (optional)</label>
311
310
  <input type="text" id="settingsNewTokenPrefixes" placeholder="chart-, brand-">
312
- <p class="hint">Comma-separated. If empty, all new keys are allowed. If set, only matching prefixes are allowed.</p>
311
+ <p class="hint">Comma-separated. Only tokens with these prefixes can be added. Leave empty to allow all new tokens.</p>
313
312
  </div>
314
313
 
315
314
  <div class="divider"></div>
@@ -410,7 +409,7 @@
410
409
  </div>
411
410
  <div id="tokenSourceInfoSync" class="repo-display" style="display:none;padding:6px 8px;">
412
411
  Last scan source: <strong id="tokenSourceLabelSync"></strong><br>
413
- Configured mode: <strong id="configuredTokenSourceModeSync">auto</strong>
412
+ Configured mode: <strong id="configuredTokenSourceModeSync">css</strong>
414
413
  </div>
415
414
 
416
415
  <p style="font-size: 11px; color: #666; margin-bottom: 12px;">
@@ -531,6 +530,7 @@
531
530
  var syncDtcgOnPushField = document.getElementById('syncDtcgOnPushField');
532
531
  var settingsAllowNewTokensFromFigma = document.getElementById('settingsAllowNewTokensFromFigma');
533
532
  var settingsNewTokenPrefixes = document.getElementById('settingsNewTokenPrefixes');
533
+ var newTokenPrefixesField = document.getElementById('newTokenPrefixesField');
534
534
  var settingsToken = document.getElementById('settingsToken');
535
535
  var settingsProjectName = document.getElementById('settingsProjectName');
536
536
  var settingsLicenseKey = document.getElementById('settingsLicenseKey');
@@ -538,7 +538,7 @@
538
538
  // Current detected changes
539
539
  var detectedChanges = { tokens: true, components: [] };
540
540
  var lastTokenSourceInfo = null;
541
- var configuredTokenSourceMode = 'auto';
541
+ var configuredTokenSourceMode = 'css';
542
542
 
543
543
  function resizeToContent() {
544
544
  // Wait for layout to settle before measuring
@@ -580,25 +580,20 @@
580
580
  }
581
581
 
582
582
  function normalizeConfiguredMode(mode) {
583
- if (mode === 'css' || mode === 'dtcg' || mode === 'auto') return mode;
584
- return 'auto';
583
+ if (mode === 'dtcg') return 'dtcg';
584
+ return 'css';
585
585
  }
586
586
 
587
587
  function applyTokenSourceModeVisibility(mode) {
588
588
  var normalized = normalizeConfiguredMode(mode);
589
- if (normalized === 'css') {
590
- tokenPathField.style.display = 'none';
591
- cssTokenPathField.style.display = 'block';
592
- syncDtcgOnPushField.style.display = 'block';
593
- return;
594
- }
595
589
  if (normalized === 'dtcg') {
596
590
  tokenPathField.style.display = 'block';
597
591
  cssTokenPathField.style.display = 'none';
598
592
  syncDtcgOnPushField.style.display = 'none';
599
593
  return;
600
594
  }
601
- tokenPathField.style.display = 'block';
595
+ // css mode
596
+ tokenPathField.style.display = 'none';
602
597
  cssTokenPathField.style.display = 'block';
603
598
  syncDtcgOnPushField.style.display = 'block';
604
599
  }
@@ -980,7 +975,8 @@
980
975
  settingsCssTokenPath.value = config.cssTokenPath || '';
981
976
  settingsSyncDtcgOnPush.checked = config.syncDtcgOnPush === true;
982
977
  settingsAllowNewTokensFromFigma.checked = config.allowNewTokensFromFigma === true;
983
- settingsNewTokenPrefixes.value = Array.isArray(config.newTokenPrefixes) ? config.newTokenPrefixes.join(', ') : '';
978
+ settingsNewTokenPrefixes.value = Array.isArray(config.newTokenPrefixes) ? config.newTokenPrefixes.join(', ') : (typeof config.newTokenPrefixes === 'string' ? config.newTokenPrefixes : '');
979
+ newTokenPrefixesField.style.display = config.allowNewTokensFromFigma === true ? 'block' : 'none';
984
980
  settingsProjectName.value = config.projectName || '';
985
981
  configuredTokenSourceMode = tokenSourceMode;
986
982
  applyTokenSourceModeVisibility(tokenSourceMode);
@@ -1197,7 +1193,7 @@
1197
1193
  repo: repo,
1198
1194
  baseBranch: settingsBranch.value.trim() || 'main',
1199
1195
  tokenPath: settingsTokenPath.value.trim() || 'design-tokens/tokens.dtcg.json',
1200
- tokenSourceMode: settingsTokenSourceMode.value || 'auto',
1196
+ tokenSourceMode: settingsTokenSourceMode.value || 'css',
1201
1197
  cssTokenPath: settingsCssTokenPath.value.trim(),
1202
1198
  syncDtcgOnPush: settingsSyncDtcgOnPush.checked === true,
1203
1199
  allowNewTokensFromFigma: settingsAllowNewTokensFromFigma.checked === true,
@@ -1217,6 +1213,10 @@
1217
1213
  applyTokenSourceModeVisibility(configuredTokenSourceMode);
1218
1214
  setTokenSourceInfo(lastTokenSourceInfo);
1219
1215
  };
1216
+
1217
+ settingsAllowNewTokensFromFigma.onchange = function() {
1218
+ newTokenPrefixesField.style.display = settingsAllowNewTokensFromFigma.checked ? 'block' : 'none';
1219
+ };
1220
1220
  </script>
1221
1221
  </body>
1222
1222
  </html>