@open-pencil/cli 0.6.0 → 0.8.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/LICENSE +21 -0
- package/package.json +7 -5
- package/src/app-client.ts +56 -0
- package/src/commands/analyze/clusters.ts +17 -77
- package/src/commands/analyze/colors.ts +28 -152
- package/src/commands/analyze/spacing.ts +18 -49
- package/src/commands/analyze/typography.ts +23 -65
- package/src/commands/eval.ts +19 -5
- package/src/commands/export.ts +95 -9
- package/src/commands/find.ts +22 -39
- package/src/commands/info.ts +18 -30
- package/src/commands/node.ts +44 -54
- package/src/commands/pages.ts +15 -24
- package/src/commands/tree.ts +30 -34
- package/src/commands/variables.ts +18 -50
package/src/commands/pages.ts
CHANGED
|
@@ -1,38 +1,29 @@
|
|
|
1
1
|
import { defineCommand } from 'citty'
|
|
2
2
|
|
|
3
3
|
import { loadDocument } from '../headless'
|
|
4
|
+
import { isAppMode, requireFile, rpc } from '../app-client'
|
|
4
5
|
import { bold, fmtList, entity, formatType } from '../format'
|
|
5
6
|
|
|
7
|
+
import type { PageItem } from '@open-pencil/core'
|
|
8
|
+
import { executeRpcCommand } from '@open-pencil/core'
|
|
9
|
+
|
|
10
|
+
async function getData(file?: string): Promise<PageItem[]> {
|
|
11
|
+
if (isAppMode(file)) return rpc<PageItem[]>('pages')
|
|
12
|
+
const graph = await loadDocument(requireFile(file))
|
|
13
|
+
return executeRpcCommand(graph, 'pages', undefined) as PageItem[]
|
|
14
|
+
}
|
|
15
|
+
|
|
6
16
|
export default defineCommand({
|
|
7
17
|
meta: { description: 'List pages in a .fig file' },
|
|
8
18
|
args: {
|
|
9
|
-
file: { type: 'positional', description: '.fig file path', required:
|
|
19
|
+
file: { type: 'positional', description: '.fig file path (omit to connect to running app)', required: false },
|
|
10
20
|
json: { type: 'boolean', description: 'Output as JSON' }
|
|
11
21
|
},
|
|
12
22
|
async run({ args }) {
|
|
13
|
-
const
|
|
14
|
-
const pages = graph.getPages()
|
|
15
|
-
|
|
16
|
-
const countNodes = (pageId: string): number => {
|
|
17
|
-
let count = 0
|
|
18
|
-
const walk = (id: string) => {
|
|
19
|
-
count++
|
|
20
|
-
const n = graph.getNode(id)
|
|
21
|
-
if (n) for (const cid of n.childIds) walk(cid)
|
|
22
|
-
}
|
|
23
|
-
const page = graph.getNode(pageId)
|
|
24
|
-
if (page) for (const cid of page.childIds) walk(cid)
|
|
25
|
-
return count
|
|
26
|
-
}
|
|
23
|
+
const pages = await getData(args.file)
|
|
27
24
|
|
|
28
25
|
if (args.json) {
|
|
29
|
-
console.log(
|
|
30
|
-
JSON.stringify(
|
|
31
|
-
pages.map((p) => ({ id: p.id, name: p.name, nodes: countNodes(p.id) })),
|
|
32
|
-
null,
|
|
33
|
-
2
|
|
34
|
-
)
|
|
35
|
-
)
|
|
26
|
+
console.log(JSON.stringify(pages, null, 2))
|
|
36
27
|
return
|
|
37
28
|
}
|
|
38
29
|
|
|
@@ -42,8 +33,8 @@ export default defineCommand({
|
|
|
42
33
|
console.log(
|
|
43
34
|
fmtList(
|
|
44
35
|
pages.map((page) => ({
|
|
45
|
-
header: entity(
|
|
46
|
-
details: { nodes:
|
|
36
|
+
header: entity('page', page.name, page.id),
|
|
37
|
+
details: { nodes: page.nodes }
|
|
47
38
|
})),
|
|
48
39
|
{ compact: true }
|
|
49
40
|
)
|
package/src/commands/tree.ts
CHANGED
|
@@ -1,59 +1,55 @@
|
|
|
1
1
|
import { defineCommand } from 'citty'
|
|
2
2
|
|
|
3
3
|
import { loadDocument } from '../headless'
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
4
|
+
import { isAppMode, requireFile, rpc } from '../app-client'
|
|
5
|
+
import { fmtTree, printError, entity, formatType } from '../format'
|
|
6
|
+
import { executeRpcCommand } from '@open-pencil/core'
|
|
7
|
+
|
|
8
|
+
import type { TreeResult, TreeNodeResult } from '@open-pencil/core'
|
|
9
|
+
import type { TreeNode } from 'agentfmt'
|
|
10
|
+
|
|
11
|
+
function toAgentfmtTree(node: TreeNodeResult, maxDepth: number, depth = 0): TreeNode {
|
|
12
|
+
const treeNode: TreeNode = {
|
|
13
|
+
header: entity(formatType(node.type), node.name, node.id)
|
|
14
|
+
}
|
|
15
|
+
if (node.children && depth < maxDepth) {
|
|
16
|
+
treeNode.children = node.children.map((c) => toAgentfmtTree(c, maxDepth, depth + 1))
|
|
17
|
+
}
|
|
18
|
+
return treeNode
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function getData(file: string | undefined, args: { page?: string; depth?: string }): Promise<TreeResult | { error: string }> {
|
|
22
|
+
const rpcArgs = { page: args.page, depth: args.depth ? Number(args.depth) : undefined }
|
|
23
|
+
if (isAppMode(file)) return rpc<TreeResult>('tree', rpcArgs)
|
|
24
|
+
const graph = await loadDocument(requireFile(file))
|
|
25
|
+
return executeRpcCommand(graph, 'tree', rpcArgs) as TreeResult | { error: string }
|
|
26
|
+
}
|
|
6
27
|
|
|
7
28
|
export default defineCommand({
|
|
8
29
|
meta: { description: 'Print the node tree' },
|
|
9
30
|
args: {
|
|
10
|
-
file: { type: 'positional', description: '.fig file path', required:
|
|
31
|
+
file: { type: 'positional', description: '.fig file path (omit to connect to running app)', required: false },
|
|
11
32
|
page: { type: 'string', description: 'Page name (default: first page)' },
|
|
12
33
|
depth: { type: 'string', description: 'Max depth (default: unlimited)' },
|
|
13
34
|
json: { type: 'boolean', description: 'Output as JSON' }
|
|
14
35
|
},
|
|
15
36
|
async run({ args }) {
|
|
16
|
-
const
|
|
17
|
-
const pages = graph.getPages()
|
|
37
|
+
const data = await getData(args.file, args)
|
|
18
38
|
const maxDepth = args.depth ? Number(args.depth) : Infinity
|
|
19
39
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
: pages[0]
|
|
23
|
-
|
|
24
|
-
if (!page) {
|
|
25
|
-
printError(`Page "${args.page}" not found. Available: ${pages.map((p) => p.name).join(', ')}`)
|
|
40
|
+
if ('error' in data) {
|
|
41
|
+
printError(data.error)
|
|
26
42
|
process.exit(1)
|
|
27
43
|
}
|
|
28
44
|
|
|
29
45
|
if (args.json) {
|
|
30
|
-
|
|
31
|
-
const node = graph.getNode(id)
|
|
32
|
-
if (!node) return null
|
|
33
|
-
const result: Record<string, unknown> = {
|
|
34
|
-
id: node.id,
|
|
35
|
-
name: node.name,
|
|
36
|
-
type: node.type,
|
|
37
|
-
x: Math.round(node.x),
|
|
38
|
-
y: Math.round(node.y),
|
|
39
|
-
width: Math.round(node.width),
|
|
40
|
-
height: Math.round(node.height)
|
|
41
|
-
}
|
|
42
|
-
if (node.childIds.length > 0 && depth < maxDepth) {
|
|
43
|
-
result.children = node.childIds.map((cid) => buildJson(cid, depth + 1)).filter(Boolean)
|
|
44
|
-
}
|
|
45
|
-
return result
|
|
46
|
-
}
|
|
47
|
-
console.log(JSON.stringify(page.childIds.map((id) => buildJson(id, 0)), null, 2))
|
|
46
|
+
console.log(JSON.stringify(data.children, null, 2))
|
|
48
47
|
return
|
|
49
48
|
}
|
|
50
49
|
|
|
51
50
|
const root = {
|
|
52
|
-
header: entity(formatType(page.type), page.name, page.id),
|
|
53
|
-
children:
|
|
54
|
-
.map((id) => graph.getNode(id))
|
|
55
|
-
.filter((n): n is SceneNode => n !== undefined)
|
|
56
|
-
.map((child) => nodeToTreeNode(graph, child, maxDepth))
|
|
51
|
+
header: entity(formatType(data.page.type), data.page.name, data.page.id),
|
|
52
|
+
children: data.children.map((c) => toAgentfmtTree(c, maxDepth))
|
|
57
53
|
}
|
|
58
54
|
|
|
59
55
|
console.log('')
|
|
@@ -1,82 +1,50 @@
|
|
|
1
1
|
import { defineCommand } from 'citty'
|
|
2
2
|
|
|
3
3
|
import { loadDocument } from '../headless'
|
|
4
|
+
import { isAppMode, requireFile, rpc } from '../app-client'
|
|
4
5
|
import { bold, fmtList, fmtSummary } from '../format'
|
|
5
|
-
import
|
|
6
|
+
import { executeRpcCommand } from '@open-pencil/core'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
const modeId = graph.getActiveModeId(variable.collectionId)
|
|
9
|
-
const raw = variable.valuesByMode[modeId]
|
|
10
|
-
if (raw === undefined) return '–'
|
|
8
|
+
import type { VariablesResult } from '@open-pencil/core'
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (typeof raw === 'object' && 'r' in raw) {
|
|
18
|
-
const { r, g, b } = raw as { r: number; g: number; b: number }
|
|
19
|
-
return (
|
|
20
|
-
'#' +
|
|
21
|
-
[r, g, b]
|
|
22
|
-
.map((c) =>
|
|
23
|
-
Math.round(c * 255)
|
|
24
|
-
.toString(16)
|
|
25
|
-
.padStart(2, '0')
|
|
26
|
-
)
|
|
27
|
-
.join('')
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return String(raw)
|
|
10
|
+
async function getData(file: string | undefined, args: { collection?: string; type?: string }): Promise<VariablesResult> {
|
|
11
|
+
const rpcArgs = { collection: args.collection, type: args.type }
|
|
12
|
+
if (isAppMode(file)) return rpc<VariablesResult>('variables', rpcArgs)
|
|
13
|
+
const graph = await loadDocument(requireFile(file))
|
|
14
|
+
return executeRpcCommand(graph, 'variables', rpcArgs) as VariablesResult
|
|
32
15
|
}
|
|
33
16
|
|
|
34
17
|
export default defineCommand({
|
|
35
18
|
meta: { description: 'List design variables and collections' },
|
|
36
19
|
args: {
|
|
37
|
-
file: { type: 'positional', description: '.fig file path', required:
|
|
20
|
+
file: { type: 'positional', description: '.fig file path (omit to connect to running app)', required: false },
|
|
38
21
|
collection: { type: 'string', description: 'Filter by collection name' },
|
|
39
22
|
type: { type: 'string', description: 'Filter by type: COLOR, FLOAT, STRING, BOOLEAN' },
|
|
40
23
|
json: { type: 'boolean', description: 'Output as JSON' }
|
|
41
24
|
},
|
|
42
25
|
async run({ args }) {
|
|
43
|
-
const
|
|
26
|
+
const data = await getData(args.file, args)
|
|
44
27
|
|
|
45
|
-
|
|
46
|
-
const variables = [...graph.variables.values()]
|
|
47
|
-
|
|
48
|
-
if (variables.length === 0) {
|
|
28
|
+
if (data.totalVariables === 0) {
|
|
49
29
|
console.log('No variables found.')
|
|
50
30
|
return
|
|
51
31
|
}
|
|
52
32
|
|
|
53
33
|
if (args.json) {
|
|
54
|
-
console.log(JSON.stringify(
|
|
34
|
+
console.log(JSON.stringify(data, null, 2))
|
|
55
35
|
return
|
|
56
36
|
}
|
|
57
37
|
|
|
58
|
-
const typeFilter = args.type?.toUpperCase()
|
|
59
|
-
const collFilter = args.collection?.toLowerCase()
|
|
60
|
-
|
|
61
38
|
console.log('')
|
|
62
39
|
|
|
63
|
-
for (const coll of collections) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const collVars = graph
|
|
67
|
-
.getVariablesForCollection(coll.id)
|
|
68
|
-
.filter((v) => !typeFilter || v.type === typeFilter)
|
|
69
|
-
|
|
70
|
-
if (collVars.length === 0) continue
|
|
71
|
-
|
|
72
|
-
const modes = coll.modes.map((m) => m.name).join(', ')
|
|
73
|
-
console.log(bold(` ${coll.name}`) + ` (${modes})`)
|
|
40
|
+
for (const coll of data.collections) {
|
|
41
|
+
console.log(bold(` ${coll.name}`) + ` (${coll.modes.join(', ')})`)
|
|
74
42
|
console.log('')
|
|
75
43
|
console.log(
|
|
76
44
|
fmtList(
|
|
77
|
-
|
|
45
|
+
coll.variables.map((v) => ({
|
|
78
46
|
header: v.name,
|
|
79
|
-
details: { value:
|
|
47
|
+
details: { value: v.value, type: v.type.toLowerCase() }
|
|
80
48
|
})),
|
|
81
49
|
{ compact: true }
|
|
82
50
|
)
|
|
@@ -86,8 +54,8 @@ export default defineCommand({
|
|
|
86
54
|
|
|
87
55
|
console.log(
|
|
88
56
|
fmtSummary({
|
|
89
|
-
variables:
|
|
90
|
-
collections:
|
|
57
|
+
variables: data.totalVariables,
|
|
58
|
+
collections: data.totalCollections
|
|
91
59
|
})
|
|
92
60
|
)
|
|
93
61
|
console.log('')
|