@ompo-design/mcp-server 0.1.3 → 0.1.4

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.
@@ -8,6 +8,7 @@ export type ApplySuggestion = {
8
8
  export type ApplyPlanFile = {
9
9
  selector: string;
10
10
  suggestions: ApplySuggestion[];
11
+ layoutIntent?: string;
11
12
  };
12
13
  export type DomStructurePlan = {
13
14
  kind: 'dom.insert' | 'dom.move' | 'dom.delete' | 'dom.flexWrap';
@@ -24,6 +25,7 @@ export type ApplyPlan = {
24
25
  domChanges: OmpoOperation[];
25
26
  warnings: string[];
26
27
  instructions: string[];
28
+ ompoGlossary: Record<string, string>;
27
29
  };
28
30
  export declare function buildApplyPlan(bundle: OmpoEditBundle): ApplyPlan;
29
31
  export declare function explainEdit(bundle: OmpoEditBundle): string;
@@ -1,10 +1,135 @@
1
- function styleSuggestions(operation) {
2
- return Object.entries(operation.changed).map(([property, value]) => ({
1
+ const OMPO_GLOSSARY = {
2
+ gap: `Ompo "Gap" is the space between children inside a flex or grid container. Apply \`gap\` on the parent layout element (not on children). The parent needs \`display: flex\` or \`display: grid\`. Example: gap "16px" → CSS \`gap: 16px\` or Tailwind \`gap-4\`.`,
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
+ fit: `Ompo "Fit" means size to content. Map \`widthMode: fit\` → \`width: fit-content\`, \`heightMode: fit\` → \`height: fit-content\` (or the project's equivalent).`
5
+ };
6
+ function formatChangedValue(property, value) {
7
+ const text = String(value);
8
+ if (text !== '')
9
+ return text;
10
+ if (property === 'width' || property === 'height') {
11
+ return '(remove fixed size — required for Fill)';
12
+ }
13
+ if (property === 'alignSelf' && text === '') {
14
+ return '(remove align-self override)';
15
+ }
16
+ return '(empty / remove)';
17
+ }
18
+ function styleSuggestionForProperty(property, value, changed) {
19
+ const parentFlex = changed.ensureParentFlex;
20
+ const parentFlexLabel = parentFlex === 'row' || parentFlex === 'column' ? parentFlex : 'row or column';
21
+ switch (property) {
22
+ case 'gap':
23
+ return {
24
+ property,
25
+ value: formatChangedValue(property, value),
26
+ strategy: 'css-rule',
27
+ notes: `${OMPO_GLOSSARY.gap} Apply on this selector if it is the flex/grid parent.`
28
+ };
29
+ case 'widthMode':
30
+ if (value === 'fill') {
31
+ return {
32
+ property,
33
+ value: 'fill',
34
+ strategy: 'css-rule',
35
+ notes: `${OMPO_GLOSSARY.fill} This selector has Fill width. If parent flex-direction is row, prefer flex-grow on this element; if column, prefer align-self: stretch for width.`
36
+ };
37
+ }
38
+ if (value === 'fit') {
39
+ return {
40
+ property,
41
+ value: 'fit',
42
+ strategy: 'css-rule',
43
+ notes: OMPO_GLOSSARY.fit
44
+ };
45
+ }
46
+ break;
47
+ case 'heightMode':
48
+ if (value === 'fill') {
49
+ return {
50
+ property,
51
+ value: 'fill',
52
+ strategy: 'css-rule',
53
+ notes: `${OMPO_GLOSSARY.fill} This selector has Fill height. If parent flex-direction is column, prefer flex-grow on this element; if row, prefer align-self: stretch for height.`
54
+ };
55
+ }
56
+ if (value === 'fit') {
57
+ return {
58
+ property,
59
+ value: 'fit',
60
+ strategy: 'css-rule',
61
+ notes: OMPO_GLOSSARY.fit
62
+ };
63
+ }
64
+ break;
65
+ case 'ensureParentFlex':
66
+ return {
67
+ property,
68
+ value: String(value),
69
+ strategy: 'css-rule',
70
+ notes: `Ompo auto-enabled flex on this element's parent so Fill sizing could work. In source, ensure the parent has display: flex and flex-direction: ${parentFlexLabel}. Apply on the parent element, not this child.`
71
+ };
72
+ case 'flexGrow':
73
+ case 'flexShrink':
74
+ case 'flexBasis':
75
+ return {
76
+ property,
77
+ value: formatChangedValue(property, value),
78
+ strategy: 'css-rule',
79
+ notes: `Part of Ompo Fill sizing on the main flex axis. Apply together with widthMode/heightMode fill and remove fixed width/height on the filled axis.`
80
+ };
81
+ case 'alignSelf':
82
+ return {
83
+ property,
84
+ value: formatChangedValue(property, value),
85
+ strategy: 'css-rule',
86
+ notes: value === 'stretch'
87
+ ? 'Ompo Fill on the cross axis: stretch this flex child to fill the parent’s cross size. Remove any fixed width/height that blocks stretching.'
88
+ : 'Map to the matching align-self rule in source.'
89
+ };
90
+ case 'minWidth':
91
+ case 'minHeight':
92
+ return {
93
+ property,
94
+ value: formatChangedValue(property, value),
95
+ strategy: 'css-rule',
96
+ notes: 'Supporting constraint for Ompo Fill sizing. Often min-width: 0 or min-height: 0 so flex children can shrink.'
97
+ };
98
+ case 'maxWidth':
99
+ case 'maxHeight':
100
+ return {
101
+ property,
102
+ value: formatChangedValue(property, value),
103
+ strategy: 'css-rule',
104
+ notes: 'Supporting constraint for Ompo Fill sizing. max-width/max-height: none allows the element to grow.'
105
+ };
106
+ }
107
+ return {
3
108
  property,
4
- value: String(value),
109
+ value: formatChangedValue(property, value),
5
110
  strategy: 'inline-style',
6
111
  notes: 'Map this changed property to the matching source stylesheet, class, or component prop. Do not modify unrelated styles.'
7
- }));
112
+ };
113
+ }
114
+ function styleSuggestions(operation) {
115
+ const changed = operation.changed;
116
+ return Object.entries(changed).map(([property, value]) => styleSuggestionForProperty(property, value, changed));
117
+ }
118
+ function summarizeLayoutIntent(changed) {
119
+ const parts = [];
120
+ if (changed.gap !== undefined) {
121
+ parts.push(`Set container gap to ${changed.gap}`);
122
+ }
123
+ if (changed.widthMode === 'fill') {
124
+ parts.push('Width: Fill (expand on main or cross axis per parent flex-direction)');
125
+ }
126
+ if (changed.heightMode === 'fill') {
127
+ parts.push('Height: Fill (expand on main or cross axis per parent flex-direction)');
128
+ }
129
+ if (changed.ensureParentFlex) {
130
+ parts.push(`Parent needs display:flex with flex-direction:${changed.ensureParentFlex}`);
131
+ }
132
+ return parts.length > 0 ? parts.join('. ') : null;
8
133
  }
9
134
  function buildDomStructurePlan(operation) {
10
135
  switch (operation.kind) {
@@ -92,6 +217,12 @@ export function buildApplyPlan(bundle) {
92
217
  suggestions: []
93
218
  };
94
219
  existing.suggestions.push(...styleSuggestions(operation));
220
+ const layoutIntent = summarizeLayoutIntent(operation.changed);
221
+ if (layoutIntent) {
222
+ existing.layoutIntent = existing.layoutIntent
223
+ ? `${existing.layoutIntent} ${layoutIntent}`
224
+ : layoutIntent;
225
+ }
95
226
  files.set(operation.selector, existing);
96
227
  if (operation.selector.includes('nth-of-type')) {
97
228
  warnings.push(`Selector "${operation.selector}" may be unstable across builds. Prefer matching by component structure or stable attributes.`);
@@ -144,10 +275,14 @@ export function buildApplyPlan(bundle) {
144
275
  instructions: [
145
276
  'Only apply properties and operations listed in this plan.',
146
277
  'Leave all untouched styling and markup unchanged.',
278
+ 'Read ompoGlossary.fill and ompoGlossary.gap before applying layout changes — Ompo terms are not literal CSS properties.',
279
+ 'When widthMode or heightMode is "fill", apply the grouped flex properties together (flexGrow, flexBasis, alignSelf, removed width/height) — do not set a fixed px width/height on the filled axis.',
280
+ 'Gap belongs on the flex/grid parent. Fill belongs on the child inside a flex parent.',
147
281
  'For dom.flexWrap: create a new wrapper, move matched children, then apply wrapperStyles.',
148
282
  'Use child anchors (tag, id, class, textSnippet) to find elements in source when selectors are unstable.',
149
283
  'Prefer the smallest possible diff for each file.'
150
- ]
284
+ ],
285
+ ompoGlossary: OMPO_GLOSSARY
151
286
  };
152
287
  }
153
288
  export function explainEdit(bundle) {
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { editsStoreReady, listEdits, readEditBundle, recordEditApplied, recordEd
8
8
  import { getOmpoEditsStorePath } from './edits-path.js';
9
9
  const server = new McpServer({
10
10
  name: 'ompo-mcp-server',
11
- version: '0.1.3'
11
+ version: '0.1.4'
12
12
  });
13
13
  function requireEditsStore() {
14
14
  const storePath = getOmpoEditsStorePath();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ompo-design/mcp-server",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "MCP server for applying Ompo visual edits to a codebase",
5
5
  "type": "module",
6
6
  "license": "MIT",