Lint CLI code: no-raw-console-format rule, fix all violations
Add no-raw-console-format lint rule for CLI commands — bans template literals and string concatenation in console.log, forcing use of agentfmt helpers (bold, dim, kv, entity, fmtTree, fmtList, etc.). Extend lint target to include packages/cli/src/. Fix all 22 lint errors in CLI code: - Replace string concatenation with agentfmt (clusters, typography, variables) - Mark optional CLI args as required: false (citty type accuracy) - Remove unnecessary type assertions and optional chains - Merge duplicate @open-pencil/core imports in headless.ts - Extract exportViaApp/exportFromFile to reduce export.ts complexity - Remove unused imports (formatType, printError) - void runMain() for floating promise
This commit is contained in:
parent
4bdc533e3d
commit
d9154ef29d
|
|
@ -147,6 +147,46 @@ const noHandRolledColor = {
|
|||
},
|
||||
}
|
||||
|
||||
const noRawConsoleFormat = {
|
||||
meta: {
|
||||
docs: {
|
||||
description:
|
||||
'Disallow hand-rolled formatting in console.log — use agentfmt helpers (bold, dim, kv, entity, fmtTree, fmtList, etc.)',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
CallExpression(node) {
|
||||
if (
|
||||
node.callee?.type !== 'MemberExpression' ||
|
||||
node.callee.object?.type !== 'Identifier' ||
|
||||
node.callee.object.name !== 'console' ||
|
||||
node.callee.property?.type !== 'Identifier' ||
|
||||
node.callee.property.name !== 'log'
|
||||
) return
|
||||
if (!node.arguments?.length) return
|
||||
|
||||
for (const arg of node.arguments) {
|
||||
if (arg.type === 'TemplateLiteral' && arg.expressions?.length > 0) {
|
||||
context.report({
|
||||
node,
|
||||
message: 'Use agentfmt helpers (bold, dim, kv, entity, etc.) instead of template literals in console.log.',
|
||||
})
|
||||
return
|
||||
}
|
||||
if (arg.type === 'BinaryExpression' && arg.operator === '+') {
|
||||
context.report({
|
||||
node,
|
||||
message: 'Use agentfmt helpers (bold, dim, kv, entity, etc.) instead of string concatenation in console.log.',
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const plugin = {
|
||||
meta: { name: 'open-pencil' },
|
||||
rules: {
|
||||
|
|
@ -154,6 +194,7 @@ const plugin = {
|
|||
'no-structuredclone-scene-arrays': noStructuredCloneSceneArrays,
|
||||
'no-math-random': noMathRandom,
|
||||
'no-hand-rolled-color': noHandRolledColor,
|
||||
'no-raw-console-format': noRawConsoleFormat,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,8 @@
|
|||
}],
|
||||
"open-pencil/no-structuredclone-scene-arrays": "error",
|
||||
"open-pencil/no-math-random": "error",
|
||||
"open-pencil/no-hand-rolled-color": "error"
|
||||
"open-pencil/no-hand-rolled-color": "error",
|
||||
"open-pencil/no-raw-console-format": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
|
@ -103,6 +104,12 @@
|
|||
"rules": {
|
||||
"max-lines": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["packages/cli/src/commands/**"],
|
||||
"rules": {
|
||||
"open-pencil/no-raw-console-format": "error"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": ["node_modules", "dist", "desktop", "*.config.*"]
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
"build": "bun run lint && vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri",
|
||||
"lint": "oxlint -c oxlint.json --type-aware --type-check src/",
|
||||
"lint": "oxlint -c oxlint.json --type-aware --type-check src/ packages/cli/src/",
|
||||
"format": "oxfmt --write src/",
|
||||
"check": "bun run lint",
|
||||
"test": "playwright test --project=openpencil",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import type { AnalyzeClustersResult } from '@open-pencil/core'
|
|||
|
||||
function calcConfidence(nodes: Array<{ width: number; height: number; childCount: number }>): number {
|
||||
if (nodes.length < 2) return 100
|
||||
const base = nodes[0]!
|
||||
const base = nodes[0]
|
||||
let score = 0
|
||||
for (const node of nodes.slice(1)) {
|
||||
const sizeDiff = Math.abs(node.width - base.width) + Math.abs(node.height - base.height)
|
||||
|
|
@ -24,7 +24,7 @@ function calcConfidence(nodes: Array<{ width: number; height: number; childCount
|
|||
|
||||
function formatSignature(sig: string): string {
|
||||
const [typeSize, children] = sig.split('|')
|
||||
const type = typeSize?.split(':')[0]
|
||||
const type = typeSize.split(':')[0]
|
||||
if (!type) return sig
|
||||
const typeName = type.charAt(0) + type.slice(1).toLowerCase()
|
||||
if (!children) return typeName
|
||||
|
|
@ -73,7 +73,7 @@ export default defineCommand({
|
|||
console.log('')
|
||||
|
||||
const items = data.clusters.map((c) => {
|
||||
const first = c.nodes[0]!
|
||||
const first = c.nodes[0]
|
||||
const confidence = calcConfidence(c.nodes)
|
||||
|
||||
const widths = c.nodes.map((n) => n.width)
|
||||
|
|
@ -102,10 +102,11 @@ export default defineCommand({
|
|||
|
||||
const clusteredNodes = data.clusters.reduce((sum, c) => sum + c.nodes.length, 0)
|
||||
console.log('')
|
||||
console.log(
|
||||
fmtSummary({ clusters: data.clusters.length }) +
|
||||
` from ${data.totalNodes} nodes (${clusteredNodes} clustered)`
|
||||
)
|
||||
console.log(fmtSummary({
|
||||
clusters: data.clusters.length,
|
||||
'total nodes': data.totalNodes,
|
||||
clustered: clusteredNodes
|
||||
}))
|
||||
console.log('')
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export default defineCommand({
|
|||
}
|
||||
|
||||
console.log('')
|
||||
console.log(fmtSummary({ 'unique styles': data.styles.length }) + ` from ${data.totalTextNodes} text nodes`)
|
||||
console.log(fmtSummary({ 'unique styles': data.styles.length, 'text nodes': data.totalTextNodes }))
|
||||
console.log('')
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,9 +6,17 @@ import { loadDocument } from '../headless'
|
|||
import { isAppMode, requireFile, rpc } from '../app-client'
|
||||
import { printError } from '../format'
|
||||
|
||||
function printResult(value: unknown, json: boolean) {
|
||||
if (json || !process.stdout.isTTY) {
|
||||
console.log(JSON.stringify(value, null, 2))
|
||||
} else {
|
||||
console.log(value)
|
||||
}
|
||||
}
|
||||
|
||||
function serializeResult(value: unknown): unknown {
|
||||
if (value === undefined || value === null) return value
|
||||
if (typeof value === 'object' && value !== null && 'toJSON' in value && typeof value.toJSON === 'function') {
|
||||
if (typeof value === 'object' && 'toJSON' in value && typeof value.toJSON === 'function') {
|
||||
return value.toJSON()
|
||||
}
|
||||
if (Array.isArray(value)) return value.map(serializeResult)
|
||||
|
|
@ -22,7 +30,7 @@ export default defineCommand({
|
|||
code: { type: 'string', alias: 'c', description: 'JavaScript code to execute' },
|
||||
stdin: { type: 'boolean', description: 'Read code from stdin' },
|
||||
write: { type: 'boolean', alias: 'w', description: 'Write changes back to the input file' },
|
||||
output: { type: 'string', alias: 'o', description: 'Write to a different file' },
|
||||
output: { type: 'string', alias: 'o', description: 'Write to a different file', required: false },
|
||||
json: { type: 'boolean', description: 'Output as JSON' },
|
||||
quiet: { type: 'boolean', alias: 'q', description: 'Suppress output' },
|
||||
},
|
||||
|
|
@ -41,13 +49,9 @@ export default defineCommand({
|
|||
}
|
||||
|
||||
if (isAppMode(args.file)) {
|
||||
const result = await rpc<unknown>('eval', { code })
|
||||
const result = await rpc('eval', { code })
|
||||
if (!args.quiet && result !== undefined && result !== null) {
|
||||
if (args.json || !process.stdout.isTTY) {
|
||||
console.log(JSON.stringify(result, null, 2))
|
||||
} else {
|
||||
console.log(result)
|
||||
}
|
||||
printResult(result, !!args.json)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -56,6 +60,7 @@ export default defineCommand({
|
|||
const graph = await loadDocument(file)
|
||||
const figma = new FigmaAPI(graph)
|
||||
|
||||
// eslint-disable-next-line no-empty-function -- needed to get AsyncFunction constructor
|
||||
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
||||
const wrappedCode = code.trim().startsWith('return')
|
||||
? code
|
||||
|
|
@ -71,17 +76,12 @@ export default defineCommand({
|
|||
}
|
||||
|
||||
if (!args.quiet && result !== undefined) {
|
||||
const serialized = serializeResult(result)
|
||||
if (args.json || !process.stdout.isTTY) {
|
||||
console.log(JSON.stringify(serialized, null, 2))
|
||||
} else {
|
||||
console.log(serialized)
|
||||
}
|
||||
printResult(serializeResult(result), !!args.json)
|
||||
}
|
||||
|
||||
if (args.write || args.output) {
|
||||
const { exportFigFile } = await import('@open-pencil/core')
|
||||
const outPath = args.output ?? file
|
||||
const outPath = args.output ? args.output : file
|
||||
const data = await exportFigFile(graph)
|
||||
await Bun.write(outPath, new Uint8Array(data))
|
||||
if (!args.quiet) {
|
||||
|
|
|
|||
|
|
@ -12,16 +12,109 @@ const RASTER_FORMATS = ['PNG', 'JPG', 'WEBP']
|
|||
const ALL_FORMATS = [...RASTER_FORMATS, 'SVG', 'JSX']
|
||||
const JSX_STYLES = ['openpencil', 'tailwind']
|
||||
|
||||
interface ExportArgs {
|
||||
file?: string
|
||||
output?: string
|
||||
format: string
|
||||
scale: string
|
||||
quality?: string
|
||||
page?: string
|
||||
node?: string
|
||||
style: string
|
||||
thumbnail?: boolean
|
||||
width: string
|
||||
height: string
|
||||
}
|
||||
|
||||
async function writeAndLog(path: string, content: string | Uint8Array) {
|
||||
await Bun.write(path, content)
|
||||
const size = typeof content === 'string' ? content.length : content.length
|
||||
console.log(ok(`Exported ${path} (${(size / 1024).toFixed(1)} KB)`))
|
||||
}
|
||||
|
||||
async function exportViaApp(format: string, args: ExportArgs) {
|
||||
if (format === 'SVG') {
|
||||
const result = await rpc<{ svg: string }>('tool', { name: 'export_svg', args: { ids: args.node ? [args.node] : undefined } })
|
||||
if (!result.svg) { printError('Nothing to export.'); process.exit(1) }
|
||||
await writeAndLog(resolve(args.output ?? 'export.svg'), result.svg)
|
||||
return
|
||||
}
|
||||
|
||||
if (format === 'JSX') {
|
||||
const result = await rpc<{ jsx: string }>('export_jsx', { nodeIds: args.node ? [args.node] : undefined, style: args.style })
|
||||
if (!result.jsx) { printError('Nothing to export.'); process.exit(1) }
|
||||
await writeAndLog(resolve(args.output ?? 'export.jsx'), result.jsx)
|
||||
return
|
||||
}
|
||||
|
||||
const result = await rpc<{ base64: string }>('export', {
|
||||
nodeIds: args.node ? [args.node] : undefined,
|
||||
scale: Number(args.scale),
|
||||
format: format.toLowerCase()
|
||||
})
|
||||
const data = Uint8Array.from(atob(result.base64), (c) => c.charCodeAt(0))
|
||||
const ext = format.toLowerCase() === 'jpg' ? 'jpg' : format.toLowerCase()
|
||||
await writeAndLog(resolve(args.output ?? `export.${ext}`), data)
|
||||
}
|
||||
|
||||
async function exportFromFile(format: string, args: ExportArgs) {
|
||||
const file = requireFile(args.file)
|
||||
const graph = await loadDocument(file)
|
||||
await loadFonts(graph)
|
||||
|
||||
const pages = graph.getPages()
|
||||
const page = args.page ? pages.find((p) => p.name === args.page) : pages[0]
|
||||
if (!page) { printError(`Page "${args.page}" not found.`); process.exit(1) }
|
||||
|
||||
const defaultName = basename(file, extname(file))
|
||||
|
||||
if (format === 'JSX') {
|
||||
const nodeIds = args.node ? [args.node] : page.childIds
|
||||
const jsxStr = nodeIds.length === 1
|
||||
? sceneNodeToJSX(nodeIds[0], graph, args.style as JSXFormat)
|
||||
: selectionToJSX(nodeIds, graph, args.style as JSXFormat)
|
||||
if (!jsxStr) { printError('Nothing to export (empty page or no visible nodes).'); process.exit(1) }
|
||||
await writeAndLog(resolve(args.output ?? `${defaultName}.jsx`), jsxStr)
|
||||
return
|
||||
}
|
||||
|
||||
const ext = format.toLowerCase() === 'jpg' ? 'jpg' : format.toLowerCase()
|
||||
const output = resolve(args.output ?? `${defaultName}.${ext}`)
|
||||
|
||||
if (format === 'SVG') {
|
||||
const nodeIds = args.node ? [args.node] : page.childIds
|
||||
const svgStr = renderNodesToSVG(graph, page.id, nodeIds)
|
||||
if (!svgStr) { printError('Nothing to export (empty page or no visible nodes).'); process.exit(1) }
|
||||
await writeAndLog(output, svgStr)
|
||||
return
|
||||
}
|
||||
|
||||
let data: Uint8Array | null
|
||||
if (args.thumbnail) {
|
||||
data = await exportThumbnail(graph, page.id, Number(args.width), Number(args.height))
|
||||
} else {
|
||||
const nodeIds = args.node ? [args.node] : page.childIds
|
||||
data = await exportNodes(graph, page.id, nodeIds, {
|
||||
scale: Number(args.scale),
|
||||
format: format as ExportFormat,
|
||||
quality: args.quality ? Number(args.quality) : undefined
|
||||
})
|
||||
}
|
||||
|
||||
if (!data) { printError('Nothing to export (empty page or no visible nodes).'); process.exit(1) }
|
||||
await writeAndLog(output, data)
|
||||
}
|
||||
|
||||
export default defineCommand({
|
||||
meta: { description: 'Export a .fig file to PNG, JPG, WEBP, SVG, or JSX' },
|
||||
args: {
|
||||
file: { type: 'positional', description: '.fig file path (omit to connect to running app)', required: false },
|
||||
output: { type: 'string', alias: 'o', description: 'Output file path (default: <name>.<format>)' },
|
||||
output: { type: 'string', alias: 'o', description: 'Output file path (default: <name>.<format>)', required: false },
|
||||
format: { type: 'string', alias: 'f', description: 'Export format: png, jpg, webp, svg, jsx (default: png)', default: 'png' },
|
||||
scale: { type: 'string', alias: 's', description: 'Export scale (default: 1)', default: '1' },
|
||||
quality: { type: 'string', alias: 'q', description: 'Quality 0-100 for JPG/WEBP (default: 90)' },
|
||||
page: { type: 'string', description: 'Page name (default: first page)' },
|
||||
node: { type: 'string', description: 'Node ID to export (default: all top-level nodes)' },
|
||||
quality: { type: 'string', alias: 'q', description: 'Quality 0-100 for JPG/WEBP (default: 90)', required: false },
|
||||
page: { type: 'string', description: 'Page name (default: first page)', required: false },
|
||||
node: { type: 'string', description: 'Node ID to export (default: all top-level nodes)', required: false },
|
||||
style: { type: 'string', description: 'JSX style: openpencil, tailwind (default: openpencil)', default: 'openpencil' },
|
||||
thumbnail: { type: 'boolean', description: 'Export page thumbnail instead of full render' },
|
||||
width: { type: 'string', description: 'Thumbnail width (default: 1920)', default: '1920' },
|
||||
|
|
@ -40,114 +133,9 @@ export default defineCommand({
|
|||
}
|
||||
|
||||
if (isAppMode(args.file)) {
|
||||
if (format === 'SVG') {
|
||||
const result = await rpc<{ svg: string }>('tool', { name: 'export_svg', args: { ids: args.node ? [args.node] : undefined } })
|
||||
if (!result.svg) {
|
||||
printError('Nothing to export.')
|
||||
process.exit(1)
|
||||
}
|
||||
const output = resolve(args.output ?? 'export.svg')
|
||||
await Bun.write(output, result.svg)
|
||||
console.log(ok(`Exported ${output} (${(result.svg.length / 1024).toFixed(1)} KB)`))
|
||||
return
|
||||
}
|
||||
|
||||
if (format === 'JSX') {
|
||||
const result = await rpc<{ jsx: string }>('export_jsx', {
|
||||
nodeIds: args.node ? [args.node] : undefined,
|
||||
style: args.style
|
||||
})
|
||||
if (!result.jsx) {
|
||||
printError('Nothing to export.')
|
||||
process.exit(1)
|
||||
}
|
||||
const output = resolve(args.output ?? 'export.jsx')
|
||||
await Bun.write(output, result.jsx)
|
||||
console.log(ok(`Exported ${output} (${(result.jsx.length / 1024).toFixed(1)} KB)`))
|
||||
return
|
||||
}
|
||||
|
||||
const result = await rpc<{ base64: string }>('export', {
|
||||
nodeIds: args.node ? [args.node] : undefined,
|
||||
scale: Number(args.scale),
|
||||
format: format.toLowerCase()
|
||||
})
|
||||
const data = Uint8Array.from(atob(result.base64), (c) => c.charCodeAt(0))
|
||||
const ext = format.toLowerCase() === 'jpg' ? 'jpg' : format.toLowerCase()
|
||||
const output = resolve(args.output ?? `export.${ext}`)
|
||||
await Bun.write(output, data)
|
||||
console.log(ok(`Exported ${output} (${(data.length / 1024).toFixed(1)} KB)`))
|
||||
return
|
||||
}
|
||||
|
||||
const file = requireFile(args.file)
|
||||
const graph = await loadDocument(file)
|
||||
await loadFonts(graph)
|
||||
|
||||
const pages = graph.getPages()
|
||||
const page = args.page
|
||||
? pages.find((p) => p.name === args.page)
|
||||
: pages[0]
|
||||
|
||||
if (!page) {
|
||||
printError(`Page "${args.page}" not found.`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const defaultName = basename(file, extname(file))
|
||||
|
||||
if (format === 'JSX') {
|
||||
const jsxFormat = args.style as JSXFormat
|
||||
const nodeIds = args.node ? [args.node] : page.childIds
|
||||
const jsxStr = nodeIds.length === 1
|
||||
? sceneNodeToJSX(nodeIds[0], graph, jsxFormat)
|
||||
: selectionToJSX(nodeIds, graph, jsxFormat)
|
||||
|
||||
if (!jsxStr) {
|
||||
printError('Nothing to export (empty page or no visible nodes).')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const output = resolve(args.output ?? `${defaultName}.jsx`)
|
||||
await Bun.write(output, jsxStr)
|
||||
console.log(ok(`Exported ${output} (${(jsxStr.length / 1024).toFixed(1)} KB)`))
|
||||
return
|
||||
}
|
||||
|
||||
const ext = format.toLowerCase() === 'jpg' ? 'jpg' : format.toLowerCase()
|
||||
const output = resolve(args.output ?? `${defaultName}.${ext}`)
|
||||
|
||||
if (format === 'SVG') {
|
||||
const nodeIds = args.node ? [args.node] : page.childIds
|
||||
const svgStr = renderNodesToSVG(graph, page.id, nodeIds)
|
||||
if (!svgStr) {
|
||||
printError('Nothing to export (empty page or no visible nodes).')
|
||||
process.exit(1)
|
||||
}
|
||||
await Bun.write(output, svgStr)
|
||||
console.log(ok(`Exported ${output} (${(svgStr.length / 1024).toFixed(1)} KB)`))
|
||||
return
|
||||
}
|
||||
|
||||
let data: Uint8Array | null
|
||||
|
||||
if (args.thumbnail) {
|
||||
data = await exportThumbnail(graph, page.id, Number(args.width), Number(args.height))
|
||||
await exportViaApp(format, args)
|
||||
} else {
|
||||
const nodeIds = args.node ? [args.node] : page.childIds
|
||||
data = await exportNodes(graph, page.id, nodeIds, {
|
||||
scale: Number(args.scale),
|
||||
format,
|
||||
quality: args.quality ? Number(args.quality) : undefined
|
||||
})
|
||||
await exportFromFile(format, args)
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
printError('Nothing to export (empty page or no visible nodes).')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
await Bun.write(output, data)
|
||||
console.log(ok(`Exported ${output} (${(data.length / 1024).toFixed(1)} KB)`))
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { defineCommand } from 'citty'
|
|||
|
||||
import { loadDocument } from '../headless'
|
||||
import { isAppMode, requireFile, rpc } from '../app-client'
|
||||
import { fmtList, printError, bold, entity, formatType } from '../format'
|
||||
import { fmtList, bold, entity, formatType } from '../format'
|
||||
import { executeRpcCommand } from '@open-pencil/core'
|
||||
|
||||
import type { FindNodeResult } from '@open-pencil/core'
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { isAppMode, requireFile, rpc } from '../app-client'
|
|||
import { fmtNode, printError, formatType } from '../format'
|
||||
import { executeRpcCommand, colorToHex } from '@open-pencil/core'
|
||||
|
||||
import type { NodeResult } from '@open-pencil/core'
|
||||
import type { Color, NodeResult } from '@open-pencil/core'
|
||||
|
||||
async function getData(file: string | undefined, id: string): Promise<NodeResult | { error: string }> {
|
||||
if (isAppMode(file)) return rpc<NodeResult>('node', { id })
|
||||
|
|
@ -47,7 +47,7 @@ export default defineCommand({
|
|||
if (data.parent) details.parent = `${data.parent.name} (${data.parent.id})`
|
||||
if (data.text) details.text = data.text
|
||||
if (data.fills.length > 0) {
|
||||
const solid = (data.fills as Array<{ type: string; visible: boolean; color: { r: number; g: number; b: number; a: number }; opacity: number }>)
|
||||
const solid = (data.fills as Array<{ type: string; visible: boolean; color: Color; opacity: number }>)
|
||||
.find((f) => f.type === 'SOLID' && f.visible)
|
||||
if (solid) {
|
||||
const hex = colorToHex(solid.color)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { defineCommand } from 'citty'
|
|||
|
||||
import { loadDocument } from '../headless'
|
||||
import { isAppMode, requireFile, rpc } from '../app-client'
|
||||
import { bold, fmtList, entity, formatType } from '../format'
|
||||
import { bold, fmtList, entity } from '../format'
|
||||
|
||||
import type { PageItem } from '@open-pencil/core'
|
||||
import { executeRpcCommand } from '@open-pencil/core'
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { defineCommand } from 'citty'
|
|||
|
||||
import { loadDocument } from '../headless'
|
||||
import { isAppMode, requireFile, rpc } from '../app-client'
|
||||
import { bold, fmtList, fmtSummary } from '../format'
|
||||
import { bold, entity, fmtList, fmtSummary } from '../format'
|
||||
import { executeRpcCommand } from '@open-pencil/core'
|
||||
|
||||
import type { VariablesResult } from '@open-pencil/core'
|
||||
|
|
@ -38,7 +38,7 @@ export default defineCommand({
|
|||
console.log('')
|
||||
|
||||
for (const coll of data.collections) {
|
||||
console.log(bold(` ${coll.name}`) + ` (${coll.modes.join(', ')})`)
|
||||
console.log(bold(entity(coll.name, coll.modes.join(', '))))
|
||||
console.log('')
|
||||
console.log(
|
||||
fmtList(
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export function formatBox(node: SceneNode): string {
|
|||
function formatFill(node: SceneNode): string | null {
|
||||
if (!node.fills.length) return null
|
||||
const solid = node.fills.find((f) => f.type === 'SOLID' && f.visible)
|
||||
if (!solid || solid.type !== 'SOLID') return null
|
||||
if (!solid?.color) return null
|
||||
const { r, g, b } = solid.color
|
||||
const hex = '#' + [r, g, b].map((c) => Math.round(c * 255).toString(16).padStart(2, '0')).join('')
|
||||
return solid.opacity < 1 ? `${hex} ${Math.round(solid.opacity * 100)}%` : hex
|
||||
|
|
@ -56,7 +56,7 @@ function formatFill(node: SceneNode): string | null {
|
|||
|
||||
function formatStroke(node: SceneNode): string | null {
|
||||
if (!node.strokes.length) return null
|
||||
const s = node.strokes[0]!
|
||||
const s = node.strokes[0]
|
||||
const { r, g, b } = s.color
|
||||
const hex = '#' + [r, g, b].map((c) => Math.round(c * 255).toString(16).padStart(2, '0')).join('')
|
||||
return `${hex} ${s.weight}px`
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ import CanvasKitInit from 'canvaskit-wasm/full'
|
|||
import type { CanvasKit } from 'canvaskit-wasm'
|
||||
import {
|
||||
parseFigFile,
|
||||
SceneGraph,
|
||||
type SceneGraph,
|
||||
type ExportFormat,
|
||||
SkiaRenderer,
|
||||
computeAllLayouts,
|
||||
loadFont,
|
||||
renderNodesToImage,
|
||||
renderThumbnail
|
||||
} from '@open-pencil/core'
|
||||
import type { ExportFormat } from '@open-pencil/core'
|
||||
|
||||
let ck: CanvasKit | null = null
|
||||
|
||||
|
|
|
|||
|
|
@ -32,4 +32,4 @@ const main = defineCommand({
|
|||
}
|
||||
})
|
||||
|
||||
runMain(main)
|
||||
void runMain(main)
|
||||
|
|
|
|||
Loading…
Reference in a new issue