@ompo-design/mcp-server 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apply-plan.d.ts +1 -1
- package/dist/apply-plan.js +108 -17
- package/dist/cli.js +2 -2
- package/dist/index.js +1 -1
- package/dist/types.d.ts +14 -1
- package/package.json +1 -1
package/dist/apply-plan.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type ApplyPlanFile = {
|
|
|
11
11
|
layoutIntent?: string;
|
|
12
12
|
};
|
|
13
13
|
export type DomStructurePlan = {
|
|
14
|
-
kind: 'dom.insert' | 'dom.move' | 'dom.delete' | 'dom.flexWrap';
|
|
14
|
+
kind: 'dom.insert' | 'dom.move' | 'dom.delete' | 'dom.flexWrap' | 'dom.iconReplace';
|
|
15
15
|
summary: string;
|
|
16
16
|
steps: string[];
|
|
17
17
|
payload: Record<string, unknown>;
|
package/dist/apply-plan.js
CHANGED
|
@@ -3,7 +3,11 @@ const OMPO_GLOSSARY = {
|
|
|
3
3
|
fill: `Ompo "Fill" means an element should expand to consume free space inside its parent. Fill is NOT a CSS property — read \`widthMode: fill\` / \`heightMode: fill\` together with \`ensureParentFlex\`, \`flexGrow\`, \`flexShrink\`, \`flexBasis\`, and \`alignSelf\` on the same selector. Parent must be \`display: flex\`. Main axis (direction of flex): use \`flex: 1 1 0\` (grow/shrink/basis). Cross axis: use \`align-self: stretch\` and remove fixed width/height on that axis. If \`ensureParentFlex\` is present, make the parent a flex container with that direction first.`,
|
|
4
4
|
fit: `Ompo "Fit" means size to content. Map \`widthMode: fit\` → \`width: fit-content\`, \`heightMode: fit\` → \`height: fit-content\` (or the project's equivalent).`,
|
|
5
5
|
imageFill: `Ompo image background fill: copy the source file from \`backgroundImageSource\` into the project (e.g. public/images/), then set \`background-image\` to a project-relative url(), with \`background-size: cover\`, \`background-position: center\`, and \`background-repeat: no-repeat\`. Do not commit file:// URLs to source.`,
|
|
6
|
-
domMove: `Ompo DOM move: reorder or reparent elements in source markup/components. Use movedElement anchors (tag, id, class, textSnippet) to find nodes. destinationChildren is the authoritative sibling order after the move — match this order in JSX/HTML or reorder mapped arrays. CSS alone cannot satisfy a dom.move
|
|
6
|
+
domMove: `Ompo DOM move: reorder or reparent elements in source markup/components. Use movedElement anchors (tag, id, class, textSnippet) to find nodes. destinationChildren is the authoritative sibling order after the move — match this order in JSX/HTML or reorder mapped arrays. CSS alone cannot satisfy a dom.move.`,
|
|
7
|
+
insertText: `Ompo text insert: the user added a new text block (usually <p>). insertedElement carries insertKind "text", a stable data-ompo-insert-id selector, and textSnippet for the current copy. Apply as markup or a text component at parent/index; check separate text operations for edits after insert.`,
|
|
8
|
+
insertIcon: `Ompo icon insert: the user added an Iconify icon (span.ompo-insert-icon) or editable SVG. iconId is the Iconify name (e.g. lucide:home) when known. Prefer the project's icon library or @iconify/react with that id instead of inlining raw SVG when possible. Size, fill color, and stroke changes appear as style operations on the same stable data-ompo-insert-id or data-ompo-icon-id selector.`,
|
|
9
|
+
replaceIcon: `Ompo icon replace: the user swapped an existing icon/SVG to a different Iconify icon. Update the project's icon component or @iconify/react usage to the new iconId — do not only patch inline SVG unless the project already inlines icons.`,
|
|
10
|
+
iconStroke: `Ompo icon/SVG stroke: iconStrokeWidth sets stroke-width on the inner SVG; iconStrokeColor sets stroke (often currentColor). Applies to inserted icons and native SVG elements selected in Ompo.`
|
|
7
11
|
};
|
|
8
12
|
function describeAnchor(anchor) {
|
|
9
13
|
const parts = [anchor.tagName];
|
|
@@ -14,10 +18,87 @@ function describeAnchor(anchor) {
|
|
|
14
18
|
if (classes)
|
|
15
19
|
parts.push(`.${classes}`);
|
|
16
20
|
}
|
|
21
|
+
if (anchor.insertKind === 'icon' && anchor.iconId) {
|
|
22
|
+
parts.push(`icon:${anchor.iconId}`);
|
|
23
|
+
}
|
|
17
24
|
if (anchor.textSnippet)
|
|
18
25
|
parts.push(`"${anchor.textSnippet}"`);
|
|
19
26
|
return parts.join(' ');
|
|
20
27
|
}
|
|
28
|
+
function buildDomInsertPlan(operation) {
|
|
29
|
+
const inserted = operation.insertedElement;
|
|
30
|
+
const isIcon = operation.insertKind === 'icon';
|
|
31
|
+
const isText = operation.insertKind === 'text';
|
|
32
|
+
const steps = [];
|
|
33
|
+
if (operation.parent) {
|
|
34
|
+
steps.push(`Locate destination parent: ${describeAnchor(operation.parent)}.`);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
steps.push(`Locate parent "${operation.parentSelector}" in source.`);
|
|
38
|
+
}
|
|
39
|
+
if (inserted) {
|
|
40
|
+
steps.push(`New element anchor: ${describeAnchor(inserted)} (selector: "${inserted.selector}").`);
|
|
41
|
+
}
|
|
42
|
+
else if (operation.selector) {
|
|
43
|
+
steps.push(`Inserted element selector: "${operation.selector}".`);
|
|
44
|
+
}
|
|
45
|
+
if (isText) {
|
|
46
|
+
steps.push(OMPO_GLOSSARY.insertText);
|
|
47
|
+
}
|
|
48
|
+
else if (isIcon) {
|
|
49
|
+
steps.push(OMPO_GLOSSARY.insertIcon);
|
|
50
|
+
if (operation.iconId) {
|
|
51
|
+
steps.push(`Iconify id: ${operation.iconId}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
steps.push(`Insert at child index ${operation.index}.`);
|
|
55
|
+
steps.push('Sanitize and adapt the HTML to the project’s component conventions before writing.');
|
|
56
|
+
const summary = isIcon
|
|
57
|
+
? `Insert Iconify icon${operation.iconId ? ` (${operation.iconId})` : ''}`
|
|
58
|
+
: isText
|
|
59
|
+
? 'Insert text block'
|
|
60
|
+
: 'Insert new element into the page';
|
|
61
|
+
return {
|
|
62
|
+
kind: 'dom.insert',
|
|
63
|
+
summary,
|
|
64
|
+
steps,
|
|
65
|
+
payload: {
|
|
66
|
+
parentSelector: operation.parentSelector,
|
|
67
|
+
insertIndex: operation.index,
|
|
68
|
+
html: operation.html,
|
|
69
|
+
selector: operation.selector,
|
|
70
|
+
insertKind: operation.insertKind,
|
|
71
|
+
iconId: operation.iconId,
|
|
72
|
+
insertedElement: operation.insertedElement,
|
|
73
|
+
parent: operation.parent
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function buildIconReplacePlan(operation) {
|
|
78
|
+
const steps = [];
|
|
79
|
+
if (operation.element) {
|
|
80
|
+
steps.push(`Locate icon element: ${describeAnchor(operation.element)} (selector: "${operation.selector}").`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
steps.push(`Locate icon element "${operation.selector}" in source.`);
|
|
84
|
+
}
|
|
85
|
+
steps.push(OMPO_GLOSSARY.replaceIcon);
|
|
86
|
+
steps.push(`Set Iconify id to ${operation.iconId}.`);
|
|
87
|
+
if (operation.previousIconId) {
|
|
88
|
+
steps.push(`Previous icon id: ${operation.previousIconId}`);
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
kind: 'dom.iconReplace',
|
|
92
|
+
summary: `Replace icon with ${operation.iconId}`,
|
|
93
|
+
steps,
|
|
94
|
+
payload: {
|
|
95
|
+
selector: operation.selector,
|
|
96
|
+
iconId: operation.iconId,
|
|
97
|
+
previousIconId: operation.previousIconId,
|
|
98
|
+
element: operation.element
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
21
102
|
function formatChildOrder(children, movedSelector) {
|
|
22
103
|
return children.map((child, index) => {
|
|
23
104
|
const marker = child.selector === movedSelector ? ' ← moved element' : '';
|
|
@@ -224,6 +305,14 @@ function styleSuggestionForProperty(property, value, changed) {
|
|
|
224
305
|
};
|
|
225
306
|
}
|
|
226
307
|
break;
|
|
308
|
+
case 'iconStrokeWidth':
|
|
309
|
+
case 'iconStrokeColor':
|
|
310
|
+
return {
|
|
311
|
+
property,
|
|
312
|
+
value: formatChangedValue(property, value),
|
|
313
|
+
strategy: 'inline-style',
|
|
314
|
+
notes: OMPO_GLOSSARY.iconStroke
|
|
315
|
+
};
|
|
227
316
|
}
|
|
228
317
|
return {
|
|
229
318
|
property,
|
|
@@ -275,23 +364,11 @@ function buildDomStructurePlan(operation) {
|
|
|
275
364
|
}
|
|
276
365
|
};
|
|
277
366
|
case 'dom.insert':
|
|
278
|
-
return
|
|
279
|
-
kind: 'dom.insert',
|
|
280
|
-
summary: 'Insert new element into the page',
|
|
281
|
-
steps: [
|
|
282
|
-
`Locate parent "${operation.parentSelector}" in source.`,
|
|
283
|
-
`Insert the provided HTML at child index ${operation.index}.`,
|
|
284
|
-
'Sanitize and adapt the HTML to the project’s component conventions before writing.'
|
|
285
|
-
],
|
|
286
|
-
payload: {
|
|
287
|
-
parentSelector: operation.parentSelector,
|
|
288
|
-
insertIndex: operation.index,
|
|
289
|
-
html: operation.html,
|
|
290
|
-
selector: operation.selector
|
|
291
|
-
}
|
|
292
|
-
};
|
|
367
|
+
return buildDomInsertPlan(operation);
|
|
293
368
|
case 'dom.move':
|
|
294
369
|
return buildDomMovePlan(operation);
|
|
370
|
+
case 'dom.iconReplace':
|
|
371
|
+
return buildIconReplacePlan(operation);
|
|
295
372
|
case 'dom.delete':
|
|
296
373
|
return {
|
|
297
374
|
kind: 'dom.delete',
|
|
@@ -396,7 +473,9 @@ export function buildApplyPlan(bundle) {
|
|
|
396
473
|
'Read ompoGlossary.domMove before applying dom.move operations.',
|
|
397
474
|
'For dom.move: destinationChildren is the structural snapshot of the final sibling order — match it in JSX/HTML or reorder mapped arrays.',
|
|
398
475
|
'For dom.flexWrap: create a new wrapper, move matched children, then apply wrapperStyles.',
|
|
399
|
-
'
|
|
476
|
+
'For dom.insert with insertKind text or icon: read ompoGlossary.insertText / insertIcon; use insertedElement anchors and iconId when applying.',
|
|
477
|
+
'For dom.iconReplace: read ompoGlossary.replaceIcon; update the project icon to the new iconId.',
|
|
478
|
+
'Use child anchors (tag, id, class, textSnippet, insertKind, iconId) to find elements in source when selectors are unstable.',
|
|
400
479
|
'Prefer the smallest possible diff for each file.'
|
|
401
480
|
],
|
|
402
481
|
ompoGlossary: OMPO_GLOSSARY
|
|
@@ -407,6 +486,9 @@ export function explainEdit(bundle) {
|
|
|
407
486
|
const textCount = bundle.operations.filter((operation) => operation.kind === 'text').length;
|
|
408
487
|
const flexWrapCount = bundle.operations.filter((operation) => operation.kind === 'dom.flexWrap').length;
|
|
409
488
|
const moveCount = bundle.operations.filter((operation) => operation.kind === 'dom.move').length;
|
|
489
|
+
const textInsertCount = bundle.operations.filter((operation) => operation.kind === 'dom.insert' && operation.insertKind === 'text').length;
|
|
490
|
+
const iconInsertCount = bundle.operations.filter((operation) => operation.kind === 'dom.insert' && operation.insertKind === 'icon').length;
|
|
491
|
+
const iconReplaceCount = bundle.operations.filter((operation) => operation.kind === 'dom.iconReplace').length;
|
|
410
492
|
const domCount = bundle.operations.length - styleCount - textCount;
|
|
411
493
|
const scopeLabel = bundle.scope.mode === 'subtree' && bundle.scope.rootLabel
|
|
412
494
|
? `${bundle.scope.rootLabel} and its children`
|
|
@@ -423,6 +505,15 @@ export function explainEdit(bundle) {
|
|
|
423
505
|
if (moveCount > 0) {
|
|
424
506
|
lines.push(`${moveCount} DOM move${moveCount === 1 ? '' : 's'} — apply via domStructurePlans (markup/children order, not CSS)`);
|
|
425
507
|
}
|
|
508
|
+
if (textInsertCount > 0) {
|
|
509
|
+
lines.push(`${textInsertCount} text insert${textInsertCount === 1 ? '' : 's'} — apply via domStructurePlans (markup at parent/index)`);
|
|
510
|
+
}
|
|
511
|
+
if (iconInsertCount > 0) {
|
|
512
|
+
lines.push(`${iconInsertCount} icon insert${iconInsertCount === 1 ? '' : 's'} — map iconId to project icons when possible`);
|
|
513
|
+
}
|
|
514
|
+
if (iconReplaceCount > 0) {
|
|
515
|
+
lines.push(`${iconReplaceCount} icon replace${iconReplaceCount === 1 ? '' : 's'} — update iconId via domStructurePlans`);
|
|
516
|
+
}
|
|
426
517
|
lines.push(`Source preview: ${bundle.source.url}`);
|
|
427
518
|
return lines.join('\n');
|
|
428
519
|
}
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { getOmpoEditsStorePath } from './edits-path.js';
|
|
|
7
7
|
import { readOmpoMcpSession } from './session.js';
|
|
8
8
|
import { readMcpTokenBalance } from './tokens.js';
|
|
9
9
|
const PACKAGE_NAME = '@ompo-design/mcp-server';
|
|
10
|
-
const PACKAGE_VERSION = '0.1.
|
|
10
|
+
const PACKAGE_VERSION = '0.1.12';
|
|
11
11
|
const SERVER_NAME = 'ompo';
|
|
12
12
|
function resolveExecutable(name) {
|
|
13
13
|
try {
|
|
@@ -187,7 +187,7 @@ async function runDoctor() {
|
|
|
187
187
|
console.log('Edits: none yet (click Send in Ompo first)');
|
|
188
188
|
}
|
|
189
189
|
console.log('');
|
|
190
|
-
console.log('If tools skip token usage, quit and reopen Claude Code to reload MCP v0.1.
|
|
190
|
+
console.log('If tools skip token usage, quit and reopen Claude Code to reload MCP v0.1.12+');
|
|
191
191
|
console.log('Global install: npx @ompo-design/mcp-server setup-global');
|
|
192
192
|
}
|
|
193
193
|
function printProjectNextSteps(projectRoot) {
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { getOmpoEditsStorePath } from './edits-path.js';
|
|
|
10
10
|
import { McpTokenError } from './tokens.js';
|
|
11
11
|
const server = new McpServer({
|
|
12
12
|
name: 'ompo-mcp-server',
|
|
13
|
-
version: '0.1.
|
|
13
|
+
version: '0.1.12'
|
|
14
14
|
});
|
|
15
15
|
function requireEditsStore() {
|
|
16
16
|
const storePath = getOmpoEditsStorePath();
|
package/dist/types.d.ts
CHANGED
|
@@ -15,6 +15,10 @@ export type DomInsertOperation = {
|
|
|
15
15
|
index: number;
|
|
16
16
|
html: string;
|
|
17
17
|
selector?: string;
|
|
18
|
+
insertKind?: 'text' | 'icon';
|
|
19
|
+
iconId?: string;
|
|
20
|
+
insertedElement?: ElementAnchor;
|
|
21
|
+
parent?: ElementAnchor;
|
|
18
22
|
};
|
|
19
23
|
export type DomMoveOperation = {
|
|
20
24
|
kind: 'dom.move';
|
|
@@ -34,12 +38,21 @@ export type DomDeleteOperation = {
|
|
|
34
38
|
kind: 'dom.delete';
|
|
35
39
|
selector: string;
|
|
36
40
|
};
|
|
41
|
+
export type DomIconReplaceOperation = {
|
|
42
|
+
kind: 'dom.iconReplace';
|
|
43
|
+
selector: string;
|
|
44
|
+
iconId: string;
|
|
45
|
+
previousIconId?: string;
|
|
46
|
+
element?: ElementAnchor;
|
|
47
|
+
};
|
|
37
48
|
export type ElementAnchor = {
|
|
38
49
|
selector: string;
|
|
39
50
|
tagName: string;
|
|
40
51
|
id?: string;
|
|
41
52
|
className?: string;
|
|
42
53
|
textSnippet?: string;
|
|
54
|
+
insertKind?: 'text' | 'icon';
|
|
55
|
+
iconId?: string;
|
|
43
56
|
};
|
|
44
57
|
export type DomFlexWrapOperation = {
|
|
45
58
|
kind: 'dom.flexWrap';
|
|
@@ -56,7 +69,7 @@ export type TextOperation = {
|
|
|
56
69
|
textContent?: string;
|
|
57
70
|
innerHTML?: string;
|
|
58
71
|
};
|
|
59
|
-
export type OmpoOperation = StyleOperation | DomInsertOperation | DomMoveOperation | DomDeleteOperation | DomFlexWrapOperation | TextOperation;
|
|
72
|
+
export type OmpoOperation = StyleOperation | DomInsertOperation | DomMoveOperation | DomDeleteOperation | DomIconReplaceOperation | DomFlexWrapOperation | TextOperation;
|
|
60
73
|
export type OmpoEditBundle = {
|
|
61
74
|
schemaVersion: number;
|
|
62
75
|
id: string;
|