Built-in design generation now runs as an agentic MCP tool-loop (reusing the agent-rs BuiltInProvider), gated behind OPENPENCIL_DESIGN_AGENT_LOOP / the Settings experimental toggle; the orchestrator stays the default. - design-agent system prompt + in-process design toolset (parity-locked with the MCP surface) + flag-gated Intent::Design routing - spawn_agents execution as sequential sub-loops + live creation-mode badges (per-agent glow + 'N/M designing...' header) - new MCP tools: get_guidelines, ToolSearch, get_screenshot, get_editor_state, export_nodes, spawn_agents; style-guide local audit - #27 AI panel restyle: rounded tool cards + green check-rings, gray user bubbles, model-pill bottom toolbar, header, empty-state pills, the PARALLEL AGENTS (agent_team_size) 1x-6x chip dropdown - multi-chat tabs: ChatSessions model (Deref-to-active) + tab row UI (switch / close / + / Cmd+T) with each run bound to its tab Large checkpoint commit spanning the working tree (Rust shell crates).
95 lines
2.8 KiB
TypeScript
95 lines
2.8 KiB
TypeScript
// apps/desktop/git/__tests__/merge-session.test.ts
|
|
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
encodeNodeConflictId,
|
|
encodeDocFieldConflictId,
|
|
parseConflictId,
|
|
buildConflictBag,
|
|
} from '../merge-session';
|
|
import type { NodeConflict, DocFieldConflict, MergeResult } from '@zseven-w/pen-core';
|
|
|
|
describe('conflict id codec', () => {
|
|
it('encodes node conflicts with the page-id placeholder for legacy single-page docs', () => {
|
|
const a: NodeConflict = {
|
|
pageId: 'page-1',
|
|
nodeId: 'rect-7',
|
|
reason: 'both-modified-same-field',
|
|
base: null,
|
|
ours: null,
|
|
theirs: null,
|
|
};
|
|
expect(encodeNodeConflictId(a)).toBe('node:page-1:rect-7');
|
|
|
|
const b: NodeConflict = {
|
|
pageId: null,
|
|
nodeId: 'frame-root',
|
|
reason: 'both-modified-same-field',
|
|
base: null,
|
|
ours: null,
|
|
theirs: null,
|
|
};
|
|
expect(encodeNodeConflictId(b)).toBe('node:_:frame-root');
|
|
});
|
|
|
|
it('encodes doc-field conflicts with field:path format', () => {
|
|
const a: DocFieldConflict = {
|
|
field: 'variables',
|
|
path: 'variables.color-1.value',
|
|
base: null,
|
|
ours: null,
|
|
theirs: null,
|
|
};
|
|
expect(encodeDocFieldConflictId(a)).toBe('field:variables:variables.color-1.value');
|
|
});
|
|
|
|
it('parses node and field ids back into structured form', () => {
|
|
expect(parseConflictId('node:page-1:rect-7')).toEqual({
|
|
kind: 'node',
|
|
pageId: 'page-1',
|
|
nodeId: 'rect-7',
|
|
});
|
|
expect(parseConflictId('node:_:frame-root')).toEqual({
|
|
kind: 'node',
|
|
pageId: null,
|
|
nodeId: 'frame-root',
|
|
});
|
|
expect(parseConflictId('field:variables:variables.color-1.value')).toEqual({
|
|
kind: 'field',
|
|
field: 'variables',
|
|
path: 'variables.color-1.value',
|
|
});
|
|
});
|
|
|
|
it('buildConflictBag attaches ids and builds the lookup map in one pass', () => {
|
|
const merged: MergeResult = {
|
|
merged: { version: '1.0.0', name: 'doc', children: [] },
|
|
nodeConflicts: [
|
|
{
|
|
pageId: null,
|
|
nodeId: 'frame-root',
|
|
reason: 'both-modified-same-field',
|
|
base: null,
|
|
ours: null,
|
|
theirs: null,
|
|
},
|
|
],
|
|
docFieldConflicts: [
|
|
{
|
|
field: 'variables',
|
|
path: 'variables.color-1.value',
|
|
base: 'red',
|
|
ours: 'blue',
|
|
theirs: 'green',
|
|
},
|
|
],
|
|
};
|
|
const { bag, conflictMap } = buildConflictBag(merged);
|
|
expect(bag.nodeConflicts).toHaveLength(1);
|
|
expect(bag.nodeConflicts[0].id).toBe('node:_:frame-root');
|
|
expect(bag.docFieldConflicts[0].id).toBe('field:variables:variables.color-1.value');
|
|
expect(conflictMap.size).toBe(2);
|
|
expect(conflictMap.get('node:_:frame-root')).toBeDefined();
|
|
expect(conflictMap.get('field:variables:variables.color-1.value')).toBeDefined();
|
|
});
|
|
});
|