Release v0.11.4

This commit is contained in:
Danila Poyarkov 2026-04-08 20:52:09 +03:00
parent 7e7036b422
commit 26a7d2d454
11 changed files with 40 additions and 19 deletions

View file

@ -2,6 +2,14 @@
## Unreleased ## Unreleased
## 0.11.4 — 2026-04-08
### Fixes
- Fix `@open-pencil/core` published package containing stale import paths from before the domain module restructuring — CLI and MCP installs from npm now resolve correctly
- Add `save_file` MCP tool for saving the current document to disk
- Clipboard text export now writes richer v4 `derivedTextData` payloads with glyph outlines for better paste fidelity
## 0.11.3 — 2026-04-08 ## 0.11.3 — 2026-04-08
### Fixes ### Fixes

View file

@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "OpenPencil", "productName": "OpenPencil",
"version": "0.11.3", "version": "0.11.4",
"identifier": "net.dannote.open-pencil", "identifier": "net.dannote.open-pencil",
"build": { "build": {
"beforeDevCommand": "bun run dev", "beforeDevCommand": "bun run dev",

View file

@ -1,7 +1,7 @@
{ {
"name": "open-pencil-app", "name": "open-pencil-app",
"private": true, "private": true,
"version": "0.11.3", "version": "0.11.4",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"workspaces": [ "workspaces": [

View file

@ -1,6 +1,6 @@
{ {
"name": "@open-pencil/cli", "name": "@open-pencil/cli",
"version": "0.11.3", "version": "0.11.4",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"bin": { "bin": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@open-pencil/core", "name": "@open-pencil/core",
"version": "0.11.3", "version": "0.11.4",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"sideEffects": false, "sideEffects": false,

View file

@ -1,5 +1,5 @@
import { resolveRGBAForPreview } from '../color/management'
import { getCanvasKit } from '../canvaskit' import { getCanvasKit } from '../canvaskit'
import { resolveRGBAForPreview } from '../color/management'
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from '../constants' import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from '../constants'
import { resolveNodeTextDirection } from '../text/direction' import { resolveNodeTextDirection } from '../text/direction'
import { import {

View file

@ -13,8 +13,8 @@ import {
makeCanvasNodeChange, makeCanvasNodeChange,
buildFontDigestMap buildFontDigestMap
} from './kiwi/serialize' } from './kiwi/serialize'
import { buildDerivedTextDataV4 } from './text/clipboard-derived-text'
import { randomInt } from './random' import { randomInt } from './random'
import { buildDerivedTextDataV4 } from './text/clipboard-derived-text'
import type { NodeChange as KiwiNodeChange } from './kiwi/codec' import type { NodeChange as KiwiNodeChange } from './kiwi/codec'
import type { InstanceNodeChange } from './kiwi/instance-overrides' import type { InstanceNodeChange } from './kiwi/instance-overrides'
@ -317,7 +317,16 @@ export async function buildFigmaClipboardHTML(
for (let i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
collectTextNodes(nodes[i]) collectTextNodes(nodes[i])
nodeChanges.push( nodeChanges.push(
...sceneNodeToKiwi(nodes[i], canvasGuid, i, localIdCounter, graph, blobs, undefined, fontDigestMap) ...sceneNodeToKiwi(
nodes[i],
canvasGuid,
i,
localIdCounter,
graph,
blobs,
undefined,
fontDigestMap
)
) )
} }

View file

@ -2,8 +2,8 @@ export const FIG_KIWI_DEFAULT_VERSION = 101
import { deflateSync, inflateSync } from 'fflate' import { deflateSync, inflateSync } from 'fflate'
import { getGlyphOutlineCommandsSync } from '../text/opentype'
import { getLoadedFontData, normalizeFontFamily, weightToStyle } from '../text/fonts' import { getLoadedFontData, normalizeFontFamily, weightToStyle } from '../text/fonts'
import { getGlyphOutlineCommandsSync } from '../text/opentype'
import { encodeVectorNetworkBlob, buildStyleOverrideTable } from '../vector' import { encodeVectorNetworkBlob, buildStyleOverrideTable } from '../vector'
import { stringToGuid, VARIABLE_BINDING_FIELDS } from './convert' import { stringToGuid, VARIABLE_BINDING_FIELDS } from './convert'
import { sceneNodeToKiwiWithContext, type KiwiNodeChange } from './node-export' import { sceneNodeToKiwiWithContext, type KiwiNodeChange } from './node-export'
@ -208,7 +208,8 @@ function buildDerivedTextData(
} }
const style = weightToStyle(node.fontWeight, node.italic) const style = weightToStyle(node.fontWeight, node.italic)
const glyphCommandLists = getGlyphOutlineCommandsSync(node.fontFamily, style, node.text, node.fontSize) ?? [] const glyphCommandLists =
getGlyphOutlineCommandsSync(node.fontFamily, style, node.text, node.fontSize) ?? []
const lineHeight = node.lineHeight ?? Math.ceil(node.fontSize * 1.2) const lineHeight = node.lineHeight ?? Math.ceil(node.fontSize * 1.2)
const glyphAdvance = node.text.length > 0 ? node.width / Math.max(node.text.length, 1) : 0 const glyphAdvance = node.text.length > 0 ? node.width / Math.max(node.text.length, 1) : 0
@ -221,8 +222,9 @@ function buildDerivedTextData(
rotation: 0 rotation: 0
})) }))
const logicalIndexToCharacterOffsetMap = Array.from({ length: node.text.length + 1 }, (_, index) => const logicalIndexToCharacterOffsetMap = Array.from(
index * glyphAdvance { length: node.text.length + 1 },
(_, index) => index * glyphAdvance
) )
return { return {
@ -234,7 +236,7 @@ function buildDerivedTextData(
position: { x: 0, y: lineHeight }, position: { x: 0, y: lineHeight },
width: node.width, width: node.width,
lineHeight, lineHeight,
lineAscent: Math.max(lineHeight - (node.fontSize * 0.2), 0) lineAscent: Math.max(lineHeight - node.fontSize * 0.2, 0)
} }
], ],
glyphs, glyphs,

View file

@ -1,8 +1,8 @@
import { normalizeFontFamily, weightToStyle } from './fonts' import { normalizeFontFamily, weightToStyle } from './fonts'
import { getGlyphOutlineCommandsSync } from './opentype' import { getGlyphOutlineCommandsSync } from './opentype'
import type { SceneNode } from '../scene-graph'
import type { NodeChange } from '../kiwi/codec' import type { NodeChange } from '../kiwi/codec'
import type { SceneNode } from '../scene-graph'
export interface ShapedClipboardText { export interface ShapedClipboardText {
lineHeight: number lineHeight: number
@ -27,7 +27,8 @@ export async function buildDerivedTextDataV4(
const normalizedFamily = normalizeFontFamily(node.fontFamily) const normalizedFamily = normalizeFontFamily(node.fontFamily)
const key = `${normalizedFamily}|${style}` const key = `${normalizedFamily}|${style}`
const lineHeightFallback = node.lineHeight ?? Math.ceil(node.fontSize * 1.2) const lineHeightFallback = node.lineHeight ?? Math.ceil(node.fontSize * 1.2)
const glyphCommandLists = getGlyphOutlineCommandsSync(node.fontFamily, style, node.text, node.fontSize) ?? [] const glyphCommandLists =
getGlyphOutlineCommandsSync(node.fontFamily, style, node.text, node.fontSize) ?? []
const glyphs = glyphCommandLists.map((commands, index) => { const glyphs = glyphCommandLists.map((commands, index) => {
const shapedGlyph = shaped?.glyphs[index] const shapedGlyph = shaped?.glyphs[index]
@ -35,7 +36,7 @@ export async function buildDerivedTextDataV4(
commands, commands,
position: { position: {
x: shapedGlyph?.x ?? 0, x: shapedGlyph?.x ?? 0,
y: shapedGlyph?.y ?? (shaped?.baseline ?? lineHeightFallback) y: shapedGlyph?.y ?? shaped?.baseline ?? lineHeightFallback
}, },
fontSize: node.fontSize, fontSize: node.fontSize,
firstCharacter: shapedGlyph?.firstCharacter ?? index, firstCharacter: shapedGlyph?.firstCharacter ?? index,
@ -53,7 +54,7 @@ export async function buildDerivedTextDataV4(
position: { x: 0, y: shaped?.baseline ?? lineHeightFallback }, position: { x: 0, y: shaped?.baseline ?? lineHeightFallback },
width: shaped?.lineWidth ?? node.width, width: shaped?.lineWidth ?? node.width,
lineHeight: shaped?.lineHeight ?? lineHeightFallback, lineHeight: shaped?.lineHeight ?? lineHeightFallback,
lineAscent: shaped?.lineAscent ?? Math.max(lineHeightFallback - (node.fontSize * 0.2), 0) lineAscent: shaped?.lineAscent ?? Math.max(lineHeightFallback - node.fontSize * 0.2, 0)
} }
], ],
glyphs, glyphs,
@ -67,7 +68,8 @@ export async function buildDerivedTextDataV4(
} }
], ],
logicalIndexToCharacterOffsetMap: logicalIndexToCharacterOffsetMap:
shaped?.logicalIndexToCharacterOffsetMap ?? Array.from({ length: node.text.length + 1 }, () => 0), shaped?.logicalIndexToCharacterOffsetMap ??
Array.from({ length: node.text.length + 1 }, () => 0),
derivedLines: [{ directionality: 'LTR' }], derivedLines: [{ directionality: 'LTR' }],
truncationStartIndex: -1, truncationStartIndex: -1,
truncatedHeight: -1 truncatedHeight: -1

View file

@ -1,6 +1,6 @@
{ {
"name": "@open-pencil/mcp", "name": "@open-pencil/mcp",
"version": "0.11.3", "version": "0.11.4",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"main": "./dist/server.js", "main": "./dist/server.js",

View file

@ -1,6 +1,6 @@
{ {
"name": "@open-pencil/vue", "name": "@open-pencil/vue",
"version": "0.11.3", "version": "0.11.4",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"exports": { "exports": {