Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions apps/desktop/src/renderer/src/lib/model/Font.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,9 +445,9 @@ export class Font {
*
* @remarks
* If the requested name already exists, a numeric suffix is appended using
* {@link nextAvailableGlyphName}. The bridge receives an explicit glyph layer
* mutation so downstream save/export paths see a real committed glyph record,
* not a UI-only placeholder.
* {@link nextAvailableGlyphName}. Glyph identity and its default-source layer
* are created as explicit bridge operations before this model rehydrates from
* the native font state.
*
* @param name - Preferred glyph name. Blank input falls back to `newGlyph`.
* @returns The handle for the glyph that was actually created.
Expand All @@ -457,13 +457,9 @@ export class Font {
const glyphName = this.nextAvailableGlyphName(name);

const handle = this.glyphHandleForName(glyphName);
this.#bridge.setXAdvance(
{
glyphHandle: handle,
sourceId: this.defaultSource.id,
},
500,
);
const unicodes = handle.unicode === undefined ? [] : [handle.unicode];
const glyphId = this.#bridge.createGlyph(glyphName, unicodes);
this.#bridge.createGlyphLayer(glyphId, this.defaultSource.id);

this.#glyphs.clear();
this.#glyphSources.clear();
Expand Down
41 changes: 27 additions & 14 deletions crates/shift-bridge/__test__/index.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,25 @@ describe("Bridge", () => {
return bridge.getSources()[0].id;
}

function defaultLayerRef(name = "A", unicode = 65) {
function defaultUnicode(name, unicode) {
return unicode ?? (name === "A" ? 65 : undefined);
}

function defaultLayerRef(name = "A", unicode) {
return {
glyphHandle: { name, unicode },
glyphHandle: { name, unicode: defaultUnicode(name, unicode) },
sourceId: defaultSourceId(),
};
}

function createDefaultLayer(name = "A", unicode) {
const resolvedUnicode = defaultUnicode(name, unicode);
const unicodes = resolvedUnicode === undefined ? [] : [resolvedUnicode];
const glyphId = bridge.createGlyph(name, unicodes);
bridge.createGlyphLayer(glyphId, defaultSourceId());
return defaultLayerRef(name, resolvedUnicode);
}

it("creates a workspace with default committed font metadata", () => {
expect(bridge.getMetadata()).toMatchObject({
familyName: "Untitled Font",
Expand All @@ -53,39 +65,40 @@ describe("Bridge", () => {
});

it("creates a new glyph through an explicit layer edit", () => {
bridge.setXAdvance(defaultLayerRef(), 500);
bridge.setXAdvance(createDefaultLayer(), 500);

expect(bridge.getGlyphs()).toEqual([
{ name: "A", unicodes: [65], componentBaseGlyphNames: [] },
]);
});

it("saves direct glyph layer edits to a shift source package target", () => {
const glyphRef = defaultLayerRef();
const glyphRef = createDefaultLayer();
const contourId = bridge.addContour(glyphRef).changed.contourIds[0];
bridge.addPoint(glyphRef, contourId, 10, 20, "onCurve", false);

const outputPath = join(tempDir, "output.shift");
const savedVersion = bridge.saveWorkspaceAs(outputPath);

expect(savedVersion).toBe(2);
expect(bridge.getPersistedVersion()).toBe(2);
expect(savedVersion).toBe(4);
expect(bridge.getPersistedVersion()).toBe(4);
expect(bridge.isDirty()).toBe(false);
expect(existsSync(outputPath)).toBe(true);
});

it("records the persisted version when saving the current workspace", () => {
bridge.addContour(defaultLayerRef());
bridge.addContour(createDefaultLayer());

const savedVersion = bridge.saveWorkspace();

expect(savedVersion).toBe(1);
expect(bridge.getPersistedVersion()).toBe(1);
expect(savedVersion).toBe(3);
expect(bridge.getPersistedVersion()).toBe(3);
expect(bridge.isDirty()).toBe(false);
});

it("exports the live workspace font through an explicit export path", async () => {
const glyphRef = defaultLayerRef();
createDefaultLayer(".notdef", undefined);
const glyphRef = createDefaultLayer();
const contourId = bridge.addContour(glyphRef).changed.contourIds[0];
bridge.addPoint(glyphRef, contourId, 10, 20, "onCurve", false);

Expand All @@ -97,7 +110,7 @@ describe("Bridge", () => {
});

it("adds a point to a contour and returns structure, values, and changed ids", () => {
const glyphRef = defaultLayerRef();
const glyphRef = createDefaultLayer();
const contourChange = bridge.addContour(glyphRef);
const contourId = contourChange.changed.contourIds[0];

Expand All @@ -120,7 +133,7 @@ describe("Bridge", () => {
});

it("applies point positions through the sparse typed-array hot path", () => {
const glyphRef = defaultLayerRef();
const glyphRef = createDefaultLayer();
const contourId = bridge.addContour(glyphRef).changed.contourIds[0];
const pointId = bridge.addPoint(glyphRef, contourId, 10, 20, "onCurve", false).changed
.pointIds[0];
Expand All @@ -141,7 +154,7 @@ describe("Bridge", () => {
});

it("restores structure and values into a glyph layer", () => {
const glyphRef = defaultLayerRef();
const glyphRef = createDefaultLayer();
const contourId = bridge.addContour(glyphRef).changed.contourIds[0];
const before = bridge.addPoint(glyphRef, contourId, 10, 20, "onCurve", false);
const pointId = before.changed.pointIds[0];
Expand All @@ -161,7 +174,7 @@ describe("Bridge", () => {
bridge.addContour({ glyphHandle: { name: "A", unicode: 65 }, sourceId: "not-a-source" }),
).toThrow(/source ID/i);

const glyphRef = defaultLayerRef();
const glyphRef = createDefaultLayer();
expect(() =>
bridge.addPoint(glyphRef, "not-a-contour", 10, 20, "onCurve", false),
).toThrow(/contour ID/i);
Expand Down
1 change: 1 addition & 0 deletions crates/shift-bridge/dts-header.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
AnchorId,
ComponentId,
GuidelineId,
GlyphId,
GlyphName,
LayerId,
SourceId,
Expand Down
3 changes: 3 additions & 0 deletions crates/shift-bridge/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
AnchorId,
ComponentId,
GuidelineId,
GlyphId,
GlyphName,
LayerId,
SourceId,
Expand All @@ -29,6 +30,8 @@ export declare class Bridge {
getSources(): Array<NapiSource>
getPersistedVersion(): number
isDirty(): boolean
createGlyph(name: GlyphName, unicodes: Array<Unicode>): GlyphId
createGlyphLayer(glyphId: GlyphId, sourceId: SourceId): LayerId
setXAdvance(glyphRef: GlyphLayerRef, width: number): NapiGlyphValueChange
translateLayer(glyphRef: GlyphLayerRef, dx: number, dy: number): NapiGlyphValueChange
addPoint(glyphRef: GlyphLayerRef, contourId: ContourId, x: number, y: number, pointType: NapiPointType, smooth: boolean): NapiGlyphStructureChange
Expand Down
Loading
Loading