feat(fig): scaffold package shell
This commit is contained in:
parent
3c04249124
commit
69ad74fd15
|
|
@ -13,6 +13,8 @@ Bun workspace packages:
|
|||
- `packages/docs` — `@open-pencil/docs`: VitePress documentation site. Run with `cd packages/docs && bun run dev`.
|
||||
- `packages/mcp` — `@open-pencil/mcp`: MCP server for AI coding tools. Stdio + HTTP (Hono). Reuses `createServer()` factory with all core tools.
|
||||
- `packages/dom-css` — `@open-pencil/dom-css`: DOM/CSS projection layer for HTML/CSS/JSX/Tailwind compatibility. Owns DesignDOM types and browser/headless CSS runtime adapters; depends on core scene-graph types but keeps DOM/CSS parser dependencies out of core.
|
||||
- `packages/kiwi` — `@open-pencil/kiwi`: pure Kiwi schema/runtime/protocol package. Owns low-level Figma Kiwi codec/container/parse helpers and stays SceneGraph-agnostic.
|
||||
- `packages/fig` — `@open-pencil/fig`: scaffold for future `.fig` document policy package. Planned home for SceneGraph ⇄ NodeChange conversion, raw metadata invalidation, and component/instance interpretation; production `.fig` APIs still live in core for now.
|
||||
|
||||
- `packages/vue` — `@open-pencil/vue`: headless Vue 3 SDK (Reka UI-style) for building custom OpenPencil-powered editor shells and embedded editing surfaces. Renderless components and composables. The app is one consumer of the SDK.
|
||||
|
||||
|
|
|
|||
16
bun.lock
16
bun.lock
|
|
@ -17,6 +17,7 @@
|
|||
"@open-pencil/cli": "workspace:*",
|
||||
"@open-pencil/core": "workspace:*",
|
||||
"@open-pencil/dom-css": "workspace:*",
|
||||
"@open-pencil/fig": "workspace:*",
|
||||
"@open-pencil/kiwi": "workspace:*",
|
||||
"@open-pencil/vue": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "^2.9.0",
|
||||
|
|
@ -176,6 +177,19 @@
|
|||
"@open-pencil/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/fig": {
|
||||
"name": "@open-pencil/fig",
|
||||
"version": "0.13.2",
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.14",
|
||||
"tsdown": "^0.21.7",
|
||||
"typescript": "~5.8.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@open-pencil/core": "workspace:*",
|
||||
"@open-pencil/kiwi": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/kiwi": {
|
||||
"name": "@open-pencil/kiwi",
|
||||
"version": "0.13.2",
|
||||
|
|
@ -798,6 +812,8 @@
|
|||
|
||||
"@open-pencil/dom-css": ["@open-pencil/dom-css@workspace:packages/dom-css"],
|
||||
|
||||
"@open-pencil/fig": ["@open-pencil/fig@workspace:packages/fig"],
|
||||
|
||||
"@open-pencil/kiwi": ["@open-pencil/kiwi@workspace:packages/kiwi"],
|
||||
|
||||
"@open-pencil/mcp": ["@open-pencil/mcp@workspace:packages/mcp"],
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
"build": "bun run build:packages && bun run lint && vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri",
|
||||
"lint": "bun run lint:structure && oxlint -c oxlint.json --type-aware --type-check src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/ packages/kiwi/src/",
|
||||
"lint:structure": "oxlint -c oxlint.json vite.config.ts vite/ src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ tests/ scripts/ tools/",
|
||||
"format": "oxfmt --write .oxfmtrc.json vite.config.ts vite/ src/ packages/core/src/ packages/cli/src/ packages/mcp/src/ packages/vue/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ tests scripts/ tools/",
|
||||
"lint": "bun run lint:structure && oxlint -c oxlint.json --type-aware --type-check src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/ packages/kiwi/src/ packages/fig/src/",
|
||||
"lint:structure": "oxlint -c oxlint.json vite.config.ts vite/ src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ packages/fig/src/ packages/fig/tests/ packages/fig/scripts/ tests/ scripts/ tools/",
|
||||
"format": "oxfmt --write .oxfmtrc.json vite.config.ts vite/ src/ packages/core/src/ packages/cli/src/ packages/mcp/src/ packages/vue/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ packages/fig/src/ packages/fig/tests/ packages/fig/scripts/ tests scripts/ tools/",
|
||||
"format:check": "bun run format && status=$(git status --porcelain -uall) && test -z \"$status\" || (echo \"$status\" && exit 1)",
|
||||
"check": "bun run build:packages && bun run lint && tsgo --noEmit && bun run check:vue && bun run check:i18n && bun run check:packages && bun run check:arch && bun run test:type-shapes && bun run test:tools && bun run test:dupes",
|
||||
"check:i18n": "bun tools/i18n/src/check-locales.ts",
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
"test:tools": "bun tools/test.ts",
|
||||
"test:dupes": "jscpd packages/core/src packages/cli/src packages/dom-css/src src --min-lines 5 --min-tokens 50 --format typescript --threshold 0",
|
||||
"test:packages": "bun tools/package-quality/src/check-metadata.ts && bun tools/package-quality/src/smoke.ts",
|
||||
"build:packages": "bun --filter @open-pencil/kiwi build && bun --filter @open-pencil/core build && bun --filter @open-pencil/dom-css build && bun --filter @open-pencil/vue build && bun --filter @open-pencil/mcp build && bun --filter @open-pencil/cli build",
|
||||
"build:packages": "bun --filter @open-pencil/kiwi build && bun --filter @open-pencil/fig build && bun --filter @open-pencil/core build && bun --filter @open-pencil/dom-css build && bun --filter @open-pencil/vue build && bun --filter @open-pencil/mcp build && bun --filter @open-pencil/cli build",
|
||||
"open-pencil": "bun packages/cli/src/index.ts",
|
||||
"docs:dev": "bun --filter @open-pencil/docs dev",
|
||||
"docs:build": "bun --filter @open-pencil/docs build",
|
||||
|
|
@ -52,6 +52,7 @@
|
|||
"@open-pencil/cli": "workspace:*",
|
||||
"@open-pencil/core": "workspace:*",
|
||||
"@open-pencil/dom-css": "workspace:*",
|
||||
"@open-pencil/fig": "workspace:*",
|
||||
"@open-pencil/kiwi": "workspace:*",
|
||||
"@open-pencil/vue": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "^2.9.0",
|
||||
|
|
|
|||
27
packages/fig/README.md
Normal file
27
packages/fig/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# @open-pencil/fig
|
||||
|
||||
`.fig` document policy package for OpenPencil.
|
||||
|
||||
This package is currently a scaffold for the next package-split stage. Use `@open-pencil/core` for production `.fig` read/write APIs until behavior moves here.
|
||||
|
||||
Planned ownership:
|
||||
|
||||
- `.fig` read/write orchestration
|
||||
- SceneGraph ⇄ Figma NodeChange conversion policy
|
||||
- raw Figma metadata preservation and invalidation
|
||||
- component/instance interpretation
|
||||
- oracle-backed `.fig` fixtures and package-local tests
|
||||
|
||||
Non-goals:
|
||||
|
||||
- low-level Kiwi schema/runtime/codec internals — use `@open-pencil/kiwi`
|
||||
- editor actions, renderer behavior, Vue/app UI, CLI formatting, or MCP transport
|
||||
|
||||
See `packages/docs/development/fig-package-plan.md` for the staged migration plan.
|
||||
|
||||
## Checks
|
||||
|
||||
```sh
|
||||
cd packages/fig
|
||||
bun run check
|
||||
```
|
||||
47
packages/fig/package.json
Normal file
47
packages/fig/package.json
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "@open-pencil/fig",
|
||||
"version": "0.13.2",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/src/index.d.ts",
|
||||
"bun": "./src/index.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/src/index.d.ts",
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "bunx tsdown --config tsdown.config.ts && bunx tsc --emitDeclarationOnly -p tsconfig.json",
|
||||
"check": "bun run typecheck && bun run test && bun run build && bun run smoke:dist",
|
||||
"prepack": "bun run build",
|
||||
"smoke:dist": "bun scripts/smoke-dist.ts",
|
||||
"test": "bun test tests",
|
||||
"typecheck": "bunx tsc --noEmit -p tsconfig.test.json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/open-pencil/open-pencil.git",
|
||||
"directory": "packages/fig"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@open-pencil/core": "workspace:*",
|
||||
"@open-pencil/kiwi": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.14",
|
||||
"tsdown": "^0.21.7",
|
||||
"typescript": "~5.8.3"
|
||||
}
|
||||
}
|
||||
7
packages/fig/scripts/smoke-dist.ts
Normal file
7
packages/fig/scripts/smoke-dist.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export {}
|
||||
|
||||
const mod = await import('../dist/index.js')
|
||||
|
||||
if (mod.FIG_PACKAGE_STATUS !== 'scaffold') {
|
||||
throw new Error('Expected @open-pencil/fig scaffold export')
|
||||
}
|
||||
25
packages/fig/src/index.ts
Normal file
25
packages/fig/src/index.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
export interface FigDocumentSource {
|
||||
readonly bytes?: Uint8Array
|
||||
readonly fileName?: string
|
||||
}
|
||||
|
||||
export interface FigDocument<Graph = unknown> {
|
||||
readonly graph: Graph
|
||||
readonly source?: FigDocumentSource
|
||||
}
|
||||
|
||||
export interface ReadFigOptions {
|
||||
readonly preserveRawMetadata?: boolean
|
||||
}
|
||||
|
||||
export interface WriteFigOptions {
|
||||
readonly source?: FigDocumentSource
|
||||
}
|
||||
|
||||
export const FIG_PACKAGE_STATUS = 'scaffold' as const
|
||||
|
||||
export function assertFigPackageReady(): void {
|
||||
throw new Error(
|
||||
'@open-pencil/fig is scaffolded for package-boundary work; use @open-pencil/core for .fig read/write APIs for now.'
|
||||
)
|
||||
}
|
||||
13
packages/fig/tests/index.test.ts
Normal file
13
packages/fig/tests/index.test.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { describe, expect, it } from 'bun:test'
|
||||
|
||||
import { FIG_PACKAGE_STATUS, assertFigPackageReady } from '../src/index'
|
||||
|
||||
describe('@open-pencil/fig package shell', () => {
|
||||
it('exports scaffold status', () => {
|
||||
expect(FIG_PACKAGE_STATUS).toBe('scaffold')
|
||||
})
|
||||
|
||||
it('directs consumers to core until behavior moves', () => {
|
||||
expect(() => assertFigPackageReady()).toThrow('@open-pencil/fig is scaffolded')
|
||||
})
|
||||
})
|
||||
13
packages/fig/tsconfig.json
Normal file
13
packages/fig/tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"outDir": "dist",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
|
||||
"types": ["bun"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
7
packages/fig/tsconfig.test.json
Normal file
7
packages/fig/tsconfig.test.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src", "tests"]
|
||||
}
|
||||
23
packages/fig/tsdown.config.ts
Normal file
23
packages/fig/tsdown.config.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { defineConfig } from 'tsdown'
|
||||
|
||||
export default defineConfig({
|
||||
entry: {
|
||||
index: './src/index.ts'
|
||||
},
|
||||
platform: 'neutral',
|
||||
format: ['esm'],
|
||||
dts: false,
|
||||
sourcemap: true,
|
||||
hash: false,
|
||||
clean: true,
|
||||
outDir: './dist',
|
||||
treeshake: {
|
||||
moduleSideEffects: false
|
||||
},
|
||||
deps: {
|
||||
onlyBundle: false
|
||||
},
|
||||
outputOptions: {
|
||||
minifyInternalExports: false
|
||||
}
|
||||
})
|
||||
|
|
@ -4,6 +4,8 @@ import { join } from 'node:path'
|
|||
const publicPackages = [
|
||||
'packages/core',
|
||||
'packages/dom-css',
|
||||
'packages/kiwi',
|
||||
'packages/fig',
|
||||
'packages/cli',
|
||||
'packages/mcp',
|
||||
'packages/vue'
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url'
|
|||
const rootDir = fileURLToPath(new URL('../../..', import.meta.url))
|
||||
const packageDirs = [
|
||||
'packages/kiwi',
|
||||
'packages/fig',
|
||||
'packages/core',
|
||||
'packages/dom-css',
|
||||
'packages/vue',
|
||||
|
|
@ -69,6 +70,7 @@ try {
|
|||
nodeEval("await import('@open-pencil/kiwi/fig/container')", tempDir)
|
||||
nodeEval("await import('@open-pencil/kiwi/fig/guid')", tempDir)
|
||||
nodeEval("await import('@open-pencil/kiwi/fig/parse')", tempDir)
|
||||
nodeEval("await import('@open-pencil/fig')", tempDir)
|
||||
nodeEval("await import('@open-pencil/core')", tempDir)
|
||||
nodeEval("await import('@open-pencil/core/scene-graph')", tempDir)
|
||||
nodeEval("await import('@open-pencil/dom-css')", tempDir)
|
||||
|
|
@ -82,10 +84,22 @@ try {
|
|||
"const { guidToString } = await import('@open-pencil/kiwi/fig/guid'); if (guidToString({ sessionID: 1, localID: 2 }) !== '1:2') throw new Error('Kiwi GUID subpath failed')",
|
||||
tempDir
|
||||
)
|
||||
nodeEval(
|
||||
"const { buildFigKiwi, parseFigKiwiChunks } = await import('@open-pencil/kiwi/fig/container'); const chunks = parseFigKiwiChunks(buildFigKiwi(new Uint8Array([1]), new Uint8Array([2]))); if (chunks?.length !== 2) throw new Error('Kiwi container subpath failed')",
|
||||
tempDir
|
||||
)
|
||||
nodeEval(
|
||||
"const { FIG_PACKAGE_STATUS } = await import('@open-pencil/fig'); if (FIG_PACKAGE_STATUS !== 'scaffold') throw new Error('Fig package smoke failed')",
|
||||
tempDir
|
||||
)
|
||||
nodeEval(
|
||||
"const { htmlToSceneGraph } = await import('@open-pencil/dom-css'); const graph = await htmlToSceneGraph('<div class=card>OpenPencil</div>', { cssText: '.card { width: 320px; }' }); if (graph.getPages()[0].width !== 320) throw new Error('DOM/CSS scene graph smoke failed')",
|
||||
tempDir
|
||||
)
|
||||
nodeEval(
|
||||
"const { jsx, jsxToDesignDocument } = await import('@open-pencil/dom-css/jsx-runtime'); const document = await jsxToDesignDocument(jsx('section', { class: 'card', style: { width: '120px' }, children: 'OpenPencil' })); const node = document.children[0]; if (node?.type !== 'element' || node.inlineStyle?.width !== '120px') throw new Error('DOM/CSS JSX runtime smoke failed')",
|
||||
tempDir
|
||||
)
|
||||
|
||||
run(['node', 'node_modules/.bin/openpencil', '--help'], tempDir)
|
||||
run(['node', 'node_modules/.bin/openpencil-mcp', '--help'], tempDir)
|
||||
|
|
|
|||
Loading…
Reference in a new issue