figma-local 1.9.0 → 2.0.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figma-local",
3
- "version": "1.9.0",
3
+ "version": "2.0.0",
4
4
  "description": "Control Figma Desktop with Claude Code. Smart read, write, and AI-prompt export. No API key required.",
5
5
  "author": "elvke",
6
6
  "license": "MIT",
@@ -11,162 +11,145 @@ allowed-tools:
11
11
 
12
12
  # Figma Library
13
13
 
14
- Access team library components and variables from other Figma files. Index libraries, search components by name, import by key, and browse design tokens.
14
+ Access team library components and variables from other Figma files. Index libraries via REST API or plugin, search components by name, import by key, and browse design tokens.
15
15
 
16
16
  ## Prerequisites
17
17
 
18
18
  - The `fig` CLI must be connected: `fig daemon status`. If not: `fig connect --safe`.
19
19
  - The Figma plugin must have `teamlibrary` permission in its manifest. If library commands fail with a permission error, re-import the plugin in Figma (Plugins → Development → Import from manifest).
20
- - Libraries must be enabled in the current Figma file (check Assets panel → team library icon).
21
20
 
22
21
  ## IMPORTANT: How to access library components
23
22
 
24
- Figma's plugin API does **not** allow browsing library components directly. To work with library components, you must **index** them first:
23
+ Figma's plugin API does **not** allow browsing library components directly. To work with library components, you must **index** them first. There are three ways to index:
25
24
 
26
- 1. Open the **library file** in Figma (the file that contains the components)
27
- 2. Run `fig library index` — this scans all pages and saves every component with its key
28
- 3. Switch to your **working file**
29
- 4. Run `fig library search --name "button"` — finds components from indexed libraries
30
- 5. Run `fig library import --key "<key>"` — imports the component
25
+ ### Option A: REST API (recommended for large files)
31
26
 
32
- ## Usage
27
+ No plugin needed. Works on any file size without hanging Figma.
33
28
 
34
- ### Index a library (run in the library file)
29
+ **First time provide token and file URL:**
30
+ ```bash
31
+ fig library index --api --token "figd_xxxxx" --file "https://www.figma.com/design/ABC123/MyFile"
32
+ ```
35
33
 
36
- Open the library/design system file in Figma, then:
34
+ The token is saved for future use. Get one from: Figma → Settings → Personal Access Tokens.
37
35
 
36
+ **After first time — just provide the file URL:**
38
37
  ```bash
39
- fig library index
38
+ fig library index --api --file "https://www.figma.com/design/ABC123/MyFile"
40
39
  ```
41
40
 
42
- This scans ALL pages in the file and saves every component (name, key, description, page, size, component set) to `~/.figma-local/libraries/<filename>.json`.
41
+ ### Option B: Page-by-page (for large files via plugin)
43
42
 
44
- Re-run this command after the library is updated to refresh the index.
45
-
46
- ### Search indexed libraries
47
-
48
- Search across all indexed libraries by component name:
43
+ Open the library file in Figma, then scan one page at a time:
49
44
 
50
45
  ```bash
51
- fig library search --name "button"
52
- fig library search --name "input"
53
- fig library search --name "card"
54
- fig library search --name "checkbox"
46
+ fig library index --page "Buttons"
47
+ fig library index --page "Inputs"
48
+ fig library index --page "Cards"
55
49
  ```
56
50
 
57
- Returns: component name, key (for importing), component set, page, size, and library name.
51
+ Each page's components are merged into the same index file. This avoids hanging on large files.
52
+
53
+ ### Option C: Full scan (small files only)
58
54
 
59
- Use `--json` for structured output:
55
+ Open the library file in Figma, then:
60
56
 
61
57
  ```bash
62
- fig library search --name "button" --json
58
+ fig library index
63
59
  ```
64
60
 
65
- ### List indexed libraries
61
+ **Warning:** This scans ALL pages at once. Only use on small files — large design systems will cause Figma to hang.
66
62
 
67
- See what libraries have been indexed:
63
+ ## After indexing: Search and Import
64
+
65
+ ### Search indexed libraries
68
66
 
69
67
  ```bash
70
- fig library list
68
+ fig library search --name "button"
69
+ fig library search --name "input"
70
+ fig library search --name "card"
71
+ fig library search --name "checkbox"
71
72
  ```
72
73
 
73
- Returns: library name, component count, page count, and when it was indexed.
74
-
75
- ### Import a component by key
74
+ Returns: component name, key (for importing), component set, page, and library name.
76
75
 
77
- Import a library component onto the canvas by its key:
76
+ ### List indexed libraries
78
77
 
79
78
  ```bash
80
- fig library import --key "abc123def456..."
79
+ fig library list
81
80
  ```
82
81
 
83
- Import and rename:
82
+ ### Import by key
84
83
 
85
84
  ```bash
86
- fig library import --key "abc123def456..." --name "PrimaryButton"
85
+ fig library import --key "<key-from-search>"
86
+ fig library import --key "<key>" --name "PrimaryButton"
87
87
  ```
88
88
 
89
- The imported component instance is placed at the viewport center and selected.
89
+ The component is placed at viewport center and selected.
90
90
 
91
- ### List library variable collections
92
-
93
- See what variable collections are available from linked libraries:
91
+ ### Inspect the imported component
94
92
 
95
93
  ```bash
96
- fig library collections
94
+ fig inspect --deep
97
95
  ```
98
96
 
99
- ### List library variables
97
+ ## Variables (no indexing needed)
100
98
 
101
- Browse all variables across all linked library collections:
99
+ Library variables are available directly via the plugin API:
102
100
 
103
101
  ```bash
104
- fig library variables
105
- fig library variables --name "color"
106
- fig library variables --name "spacing"
102
+ fig library collections # List variable collections
103
+ fig library variables # List all variables
104
+ fig library variables --name "color" # Search by name
107
105
  ```
108
106
 
109
- ### List components on current page
107
+ ## Components on current page
110
108
 
111
- Find library components that are already used on the current page:
109
+ Find library components already dragged onto the current page:
112
110
 
113
111
  ```bash
114
112
  fig library components
115
113
  fig library components --name "button"
116
114
  ```
117
115
 
118
- ### JSON output
119
-
120
- All commands support `--json` for structured output.
116
+ ## Full workflow
121
117
 
122
- ## Workflow: Building UI with library components
123
-
124
- 1. **Index** the library (one-time, in the library file):
118
+ 1. **Index** the library (pick one method):
125
119
  ```bash
126
- fig library index
120
+ # Best for large files:
121
+ fig library index --api --token "figd_..." --file "https://..."
122
+ # Or page by page:
123
+ fig library index --page "Buttons"
127
124
  ```
128
125
 
129
- 2. **Switch** to your working file in Figma.
130
-
131
- 3. **Search** for the components you need:
126
+ 2. **Search** for components:
132
127
  ```bash
133
- fig library search --name "button"
134
- fig library search --name "input"
128
+ fig library search --name "button" --json
135
129
  ```
136
130
 
137
- 4. **Import** each component by key:
131
+ 3. **Import** into your working file:
138
132
  ```bash
139
- fig library import --key "<key-from-search>"
133
+ fig library import --key "<key>"
140
134
  ```
141
135
 
142
- 5. **Inspect** the imported component to get its full specs:
136
+ 4. **Inspect** for full specs:
143
137
  ```bash
144
138
  fig inspect --deep
145
139
  ```
146
140
 
147
- 6. **Get variables** for design tokens:
141
+ 5. **Get variables** for tokens:
148
142
  ```bash
149
143
  fig library variables --name "primary" --json
150
144
  ```
151
145
 
152
- 7. **Replicate** in code using the exact specs and token values.
153
-
154
- ## Workflow: Extracting a full design system
155
-
156
- 1. Open the design system file → `fig library index`
157
- 2. Get all components: `fig library search --name "" --json`
158
- 3. Get all variables: `fig library variables --json`
159
- 4. Import key components one by one and document them:
160
- ```bash
161
- fig library import --key "<key>"
162
- fig document --json
163
- ```
146
+ 6. **Replicate** in code.
164
147
 
165
148
  ## Tips
166
149
 
167
- - **Index once, search many times** the index is saved locally and persists across sessions.
168
- - Re-index when the library is updated: open the library file → `fig library index`.
169
- - `fig library components` only finds components already on the current page. Use `search` for the full library.
170
- - After importing a component, use `fig inspect --deep` to get full specs including variable bindings.
150
+ - **REST API is fastest** for large design systems no file opening needed.
151
+ - Token is saved after first use in `~/.figma-local/figma-token`.
152
+ - Page-by-page indexing merges results run multiple times to build up the index.
153
+ - Re-index when the library is updated to get new components.
154
+ - `fig library search --name "" --json` returns ALL indexed components.
171
155
  - Component keys are stable across file versions — save them for repeated imports.
172
- - Use `fig library search --name "button" --json | jq '.[].key'` to extract just the keys.
package/src/index.js CHANGED
@@ -10581,27 +10581,34 @@ program
10581
10581
  .option('--key <key>', 'Component key for importing')
10582
10582
  .option('--name <name>', 'Filter by name (partial match)')
10583
10583
  .option('--json', 'Output raw JSON')
10584
+ .option('--api', 'Use Figma REST API instead of plugin (for index)')
10585
+ .option('--token <token>', 'Figma personal access token (for --api)')
10586
+ .option('--file <url>', 'Figma file URL (for --api)')
10587
+ .option('--page <pageName>', 'Index only a specific page (for index)')
10584
10588
  .addHelpText('after', `
10585
10589
  Actions:
10586
10590
  collections List all available library variable collections
10587
10591
  variables List variables from a library collection (use --name to filter)
10588
10592
  components List available library components on current page (use --name to filter)
10589
- index Scan the current open file and save all components to a local index
10593
+ index Scan and save all components to a local index
10590
10594
  search Search indexed libraries for components (use --name to filter)
10591
10595
  import Import a component by key (use --key)
10592
10596
  list List all indexed libraries
10593
10597
 
10598
+ Index modes:
10599
+ fig library index Scan current file via plugin (small files)
10600
+ fig library index --page "Buttons" Scan only one page (large files)
10601
+ fig library index --api --token "figd_..." --file "URL" Use REST API (best for large files)
10602
+ fig library index --api --file "URL" Use saved token
10603
+
10594
10604
  Examples:
10595
10605
  fig library collections List all library variable collections
10596
- fig library variables List all library variables
10597
10606
  fig library variables --name "color" List variables matching "color"
10598
- fig library components List available library components
10599
10607
  fig library components --name "button" Find button components
10600
- fig library index Index all components in the current open file
10608
+ fig library index --api --token "figd_..." --file "https://www.figma.com/design/ABC/..."
10601
10609
  fig library search --name "button" Search indexed libraries for "button"
10602
10610
  fig library list List all indexed libraries
10603
10611
  fig library import --key "abc123..." Import a component by its key
10604
- fig library import --key "abc123..." --name "MyButton" Import and rename
10605
10612
  `)
10606
10613
  .action(async (action, options) => {
10607
10614
  checkConnection();
@@ -10794,13 +10801,214 @@ Examples:
10794
10801
  console.log(` ${chalk.gray('id:')} ${result.id}`);
10795
10802
  console.log(chalk.gray(' Component is now selected on the canvas.'));
10796
10803
  } else if (action === 'index') {
10797
- spinner.text = 'Scanning all pages for components...';
10798
- const code = `(function() {
10804
+ // Helper to save index data and print summary
10805
+ function saveIndexAndPrint(result, opts) {
10806
+ const libDir = join(homedir(), '.figma-local', 'libraries');
10807
+ mkdirSync(libDir, { recursive: true });
10808
+ const safeName = result.fileName.replace(/[^a-zA-Z0-9_-]/g, '_').toLowerCase();
10809
+ const libFile = join(libDir, `${safeName}.json`);
10810
+
10811
+ // If file exists and we're doing page-by-page, merge
10812
+ let existingComponents = [];
10813
+ let existingPages = new Set();
10814
+ if (opts.page && existsSync(libFile)) {
10815
+ try {
10816
+ const existing = JSON.parse(readFileSync(libFile, 'utf8'));
10817
+ // Keep components from OTHER pages
10818
+ existingComponents = (existing.components || []).filter(c => c.page !== opts.page);
10819
+ existing.components.forEach(c => existingPages.add(c.page));
10820
+ } catch (e) { /* fresh start */ }
10821
+ }
10822
+ const mergedComponents = [...existingComponents, ...result.components];
10823
+ result.components.forEach(c => existingPages.add(c.page));
10824
+
10825
+ const indexData = {
10826
+ fileName: result.fileName,
10827
+ fileKey: result.fileKey || '',
10828
+ indexedAt: new Date().toISOString(),
10829
+ pageCount: opts.page ? existingPages.size : result.pageCount,
10830
+ componentCount: mergedComponents.length,
10831
+ components: mergedComponents
10832
+ };
10833
+ writeFileSync(libFile, JSON.stringify(indexData, null, 2));
10834
+ return { indexData, libFile };
10835
+ }
10836
+
10837
+ function printIndexSummary(indexData, libFile, opts) {
10838
+ console.log(` ${chalk.gray('Saved to:')} ${libFile}`);
10839
+ if (opts.json) {
10840
+ console.log(JSON.stringify(indexData, null, 2));
10841
+ } else {
10842
+ const sets = {};
10843
+ const standalone = [];
10844
+ for (const c of indexData.components) {
10845
+ if (c.componentSet) {
10846
+ if (!sets[c.componentSet]) sets[c.componentSet] = [];
10847
+ sets[c.componentSet].push(c);
10848
+ } else {
10849
+ standalone.push(c);
10850
+ }
10851
+ }
10852
+ if (Object.keys(sets).length > 0) {
10853
+ console.log(chalk.cyan('\n Component Sets:'));
10854
+ for (const [setName, variants] of Object.entries(sets)) {
10855
+ console.log(` ${chalk.white(setName)} ${chalk.gray(`(${variants.length} variant${variants.length !== 1 ? 's' : ''})`)}`);
10856
+ }
10857
+ }
10858
+ if (standalone.length > 0) {
10859
+ console.log(chalk.cyan('\n Standalone Components:'));
10860
+ for (const c of standalone.slice(0, 50)) {
10861
+ console.log(` ${chalk.white(c.name)} ${chalk.gray(`[${c.page}]`)}`);
10862
+ }
10863
+ if (standalone.length > 50) {
10864
+ console.log(chalk.gray(` ... and ${standalone.length - 50} more`));
10865
+ }
10866
+ }
10867
+ }
10868
+ }
10869
+
10870
+ // ---- REST API mode ----
10871
+ if (options.api) {
10872
+ // Get or save token
10873
+ const configDir = join(homedir(), '.figma-local');
10874
+ mkdirSync(configDir, { recursive: true });
10875
+ const tokenFile = join(configDir, 'figma-token');
10876
+ let token = options.token || '';
10877
+ if (!token && existsSync(tokenFile)) {
10878
+ token = readFileSync(tokenFile, 'utf8').trim();
10879
+ }
10880
+ if (!token) {
10881
+ spinner.fail('Figma token required. Get one from: Figma → Settings → Personal Access Tokens\nThen run: fig library index --api --token "figd_..." --file "URL"');
10882
+ process.exit(1);
10883
+ }
10884
+ // Save token for future use
10885
+ if (options.token) {
10886
+ writeFileSync(tokenFile, token);
10887
+ }
10888
+
10889
+ if (!options.file) {
10890
+ spinner.fail('--file is required with --api. Provide a Figma file URL.\nExample: fig library index --api --file "https://www.figma.com/design/ABC123/MyFile"');
10891
+ process.exit(1);
10892
+ }
10893
+
10894
+ // Extract file key from URL
10895
+ const fileKeyMatch = options.file.match(/(?:file|design)\/([a-zA-Z0-9]+)/);
10896
+ if (!fileKeyMatch) {
10897
+ spinner.fail('Could not extract file key from URL. Use a URL like: https://www.figma.com/design/ABC123/MyFile');
10898
+ process.exit(1);
10899
+ }
10900
+ const fileKey = fileKeyMatch[1];
10901
+
10902
+ spinner.text = `Fetching file metadata from Figma API...`;
10903
+
10904
+ // Fetch the file from the REST API
10905
+ const https = await import('https');
10906
+ function figmaApiGet(endpoint) {
10907
+ return new Promise((resolve, reject) => {
10908
+ const url = `https://api.figma.com/v1${endpoint}`;
10909
+ const req = https.get(url, { headers: { 'X-Figma-Token': token } }, (res) => {
10910
+ let data = '';
10911
+ res.on('data', chunk => data += chunk);
10912
+ res.on('end', () => {
10913
+ if (res.statusCode === 403) {
10914
+ reject(new Error('Access denied. Check your token has read access to this file.'));
10915
+ } else if (res.statusCode === 404) {
10916
+ reject(new Error('File not found. Check the URL is correct.'));
10917
+ } else if (res.statusCode !== 200) {
10918
+ reject(new Error(`Figma API returned ${res.statusCode}: ${data.slice(0, 200)}`));
10919
+ } else {
10920
+ try { resolve(JSON.parse(data)); }
10921
+ catch (e) { reject(new Error('Invalid JSON response from Figma API')); }
10922
+ }
10923
+ });
10924
+ });
10925
+ req.on('error', reject);
10926
+ });
10927
+ }
10928
+
10799
10929
  try {
10800
- var components = [];
10801
- var pages = figma.root.children;
10802
- for (var p = 0; p < pages.length; p++) {
10803
- var page = pages[p];
10930
+ // Get file components via the components endpoint (lightweight)
10931
+ spinner.text = 'Fetching components from Figma API...';
10932
+ const compData = await figmaApiGet(`/files/${fileKey}/components`);
10933
+
10934
+ const components = [];
10935
+ if (compData.meta && compData.meta.components) {
10936
+ for (const comp of compData.meta.components) {
10937
+ const c = {
10938
+ name: comp.name,
10939
+ key: comp.key,
10940
+ id: comp.node_id,
10941
+ page: comp.containing_frame ? comp.containing_frame.pageName || '' : '',
10942
+ description: comp.description || '',
10943
+ };
10944
+ if (comp.containing_frame && comp.containing_frame.name) {
10945
+ // The containing_frame for component set variants shows the set name
10946
+ c.componentSet = comp.containing_frame.name !== comp.name ? comp.containing_frame.name : undefined;
10947
+ }
10948
+ components.push(c);
10949
+ }
10950
+ }
10951
+
10952
+ // Also get component sets
10953
+ spinner.text = 'Fetching component sets from Figma API...';
10954
+ try {
10955
+ const setsData = await figmaApiGet(`/files/${fileKey}/component_sets`);
10956
+ if (setsData.meta && setsData.meta.component_sets) {
10957
+ for (const set of setsData.meta.component_sets) {
10958
+ // Mark components that belong to this set
10959
+ for (const comp of components) {
10960
+ if (comp.id && set.node_id && comp.componentSet === undefined) {
10961
+ // Check if node is child of this set by matching containing_frame
10962
+ }
10963
+ }
10964
+ }
10965
+ }
10966
+ } catch (e) { /* component_sets endpoint may not exist for all plans */ }
10967
+
10968
+ // Get file name
10969
+ spinner.text = 'Fetching file info...';
10970
+ let fileName = fileKey;
10971
+ try {
10972
+ const fileInfo = await figmaApiGet(`/files/${fileKey}?depth=1`);
10973
+ if (fileInfo.name) fileName = fileInfo.name;
10974
+ } catch (e) { /* use fileKey as fallback */ }
10975
+
10976
+ const result = {
10977
+ fileName,
10978
+ fileKey,
10979
+ pageCount: 0,
10980
+ componentCount: components.length,
10981
+ components
10982
+ };
10983
+
10984
+ const { indexData, libFile } = saveIndexAndPrint(result, options);
10985
+ spinner.succeed(`Indexed ${components.length} components from "${fileName}" via API`);
10986
+ printIndexSummary(indexData, libFile, options);
10987
+
10988
+ } catch (e) {
10989
+ spinner.fail(`Figma API error: ${e.message}`);
10990
+ process.exit(1);
10991
+ }
10992
+
10993
+ // ---- Plugin mode with --page filter ----
10994
+ } else if (options.page) {
10995
+ const pageName = options.page;
10996
+ spinner.text = `Scanning page "${pageName}" for components...`;
10997
+ const safePageName = pageName.replace(/'/g, "\\'");
10998
+ const code = `(function() {
10999
+ try {
11000
+ var components = [];
11001
+ var targetPage = null;
11002
+ var pages = figma.root.children;
11003
+ for (var p = 0; p < pages.length; p++) {
11004
+ if (pages[p].name === '${safePageName}' || pages[p].name.toLowerCase().indexOf('${safePageName.toLowerCase()}') !== -1) {
11005
+ targetPage = pages[p];
11006
+ break;
11007
+ }
11008
+ }
11009
+ if (!targetPage) {
11010
+ return { error: 'Page "${safePageName}" not found. Available pages: ' + pages.map(function(p) { return p.name; }).join(', ') };
11011
+ }
10804
11012
  function scanNode(node, pageName) {
10805
11013
  if (node.type === 'COMPONENT') {
10806
11014
  var comp = {
@@ -10823,66 +11031,79 @@ Examples:
10823
11031
  }
10824
11032
  }
10825
11033
  }
10826
- scanNode(page, page.name);
11034
+ scanNode(targetPage, targetPage.name);
11035
+ return {
11036
+ fileName: figma.root.name,
11037
+ fileKey: figma.fileKey || '',
11038
+ pageCount: 1,
11039
+ componentCount: components.length,
11040
+ components: components
11041
+ };
11042
+ } catch(e) {
11043
+ return { error: e.message || 'Failed to scan page for components.' };
10827
11044
  }
10828
- return {
10829
- fileName: figma.root.name,
10830
- fileKey: figma.fileKey || '',
10831
- pageCount: pages.length,
10832
- componentCount: components.length,
10833
- components: components
10834
- };
10835
- } catch(e) {
10836
- return { error: e.message || 'Failed to scan file for components.' };
11045
+ })()`;
11046
+ const result = await daemonExec('eval', { code });
11047
+ if (result.error) {
11048
+ spinner.fail(result.error);
11049
+ process.exit(1);
10837
11050
  }
10838
- })()`;
10839
- const result = await daemonExec('eval', { code });
10840
- if (result.error) {
10841
- spinner.fail(result.error);
10842
- process.exit(1);
10843
- }
10844
- // Save to ~/.figma-local/libraries/
10845
- const libDir = join(homedir(), '.figma-local', 'libraries');
10846
- mkdirSync(libDir, { recursive: true });
10847
- const safeName = result.fileName.replace(/[^a-zA-Z0-9_-]/g, '_').toLowerCase();
10848
- const libFile = join(libDir, `${safeName}.json`);
10849
- const indexData = {
10850
- fileName: result.fileName,
10851
- fileKey: result.fileKey,
10852
- indexedAt: new Date().toISOString(),
10853
- pageCount: result.pageCount,
10854
- componentCount: result.componentCount,
10855
- components: result.components
10856
- };
10857
- writeFileSync(libFile, JSON.stringify(indexData, null, 2));
10858
- spinner.succeed(`Indexed ${result.componentCount} components from "${result.fileName}" (${result.pageCount} pages)`);
10859
- console.log(` ${chalk.gray('Saved to:')} ${libFile}`);
10860
- if (options.json) {
10861
- console.log(JSON.stringify(indexData, null, 2));
11051
+ const { indexData, libFile } = saveIndexAndPrint(result, { page: pageName });
11052
+ spinner.succeed(`Indexed ${result.componentCount} components from page "${pageName}" (${indexData.componentCount} total in library)`);
11053
+ printIndexSummary(indexData, libFile, options);
11054
+
11055
+ // ---- Plugin mode full scan ----
10862
11056
  } else {
10863
- // Show summary by component set
10864
- const sets = {};
10865
- const standalone = [];
10866
- for (const c of result.components) {
10867
- if (c.componentSet) {
10868
- if (!sets[c.componentSet]) sets[c.componentSet] = [];
10869
- sets[c.componentSet].push(c);
10870
- } else {
10871
- standalone.push(c);
10872
- }
10873
- }
10874
- if (Object.keys(sets).length > 0) {
10875
- console.log(chalk.cyan('\n Component Sets:'));
10876
- for (const [setName, variants] of Object.entries(sets)) {
10877
- console.log(` ${chalk.white(setName)} ${chalk.gray(`(${variants.length} variant${variants.length !== 1 ? 's' : ''})`)}`);
10878
- }
10879
- }
10880
- if (standalone.length > 0) {
10881
- console.log(chalk.cyan('\n Standalone Components:'));
10882
- for (const c of standalone) {
10883
- console.log(` ${chalk.white(c.name)} ${chalk.gray(`[${c.page}]`)}`);
11057
+ spinner.text = 'Scanning all pages for components...';
11058
+ const code = `(function() {
11059
+ try {
11060
+ var components = [];
11061
+ var pages = figma.root.children;
11062
+ for (var p = 0; p < pages.length; p++) {
11063
+ var page = pages[p];
11064
+ function scanNode(node, pageName) {
11065
+ if (node.type === 'COMPONENT') {
11066
+ var comp = {
11067
+ name: node.name,
11068
+ key: node.key,
11069
+ id: node.id,
11070
+ page: pageName,
11071
+ description: node.description || '',
11072
+ w: Math.round(node.width),
11073
+ h: Math.round(node.height)
11074
+ };
11075
+ if (node.parent && node.parent.type === 'COMPONENT_SET') {
11076
+ comp.componentSet = node.parent.name;
11077
+ }
11078
+ components.push(comp);
11079
+ }
11080
+ if ('children' in node) {
11081
+ for (var i = 0; i < node.children.length; i++) {
11082
+ scanNode(node.children[i], pageName);
11083
+ }
11084
+ }
11085
+ }
11086
+ scanNode(page, page.name);
11087
+ }
11088
+ return {
11089
+ fileName: figma.root.name,
11090
+ fileKey: figma.fileKey || '',
11091
+ pageCount: pages.length,
11092
+ componentCount: components.length,
11093
+ components: components
11094
+ };
11095
+ } catch(e) {
11096
+ return { error: e.message || 'Failed to scan file for components.' };
10884
11097
  }
11098
+ })()`;
11099
+ const result = await daemonExec('eval', { code });
11100
+ if (result.error) {
11101
+ spinner.fail(result.error);
11102
+ process.exit(1);
10885
11103
  }
11104
+ const { indexData, libFile } = saveIndexAndPrint(result, {});
11105
+ spinner.succeed(`Indexed ${result.componentCount} components from "${result.fileName}" (${result.pageCount} pages)`);
11106
+ printIndexSummary(indexData, libFile, options);
10886
11107
  }
10887
11108
  } else if (action === 'search') {
10888
11109
  const nameFilter = options.name ? options.name.toLowerCase() : '';