modscape 1.0.8 → 1.1.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modscape",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Modscape: A YAML-driven data modeling visualizer CLI",
5
5
  "repository": {
6
6
  "type": "git",
package/src/export.js CHANGED
@@ -15,6 +15,7 @@ function normalizeSchema(data) {
15
15
  tables: Array.isArray(data.tables) ? data.tables : [],
16
16
  relationships: Array.isArray(data.relationships) ? data.relationships : [],
17
17
  domains: Array.isArray(data.domains) ? data.domains : [],
18
+ annotations: Array.isArray(data.annotations) ? data.annotations : [],
18
19
  layout: data.layout || {}
19
20
  };
20
21
 
@@ -30,11 +31,18 @@ function normalizeSchema(data) {
30
31
  }
31
32
  }
32
33
 
34
+ const columns = (Array.isArray(table.columns) ? table.columns : []).map(col => ({
35
+ ...col,
36
+ id: col.id || 'unknown',
37
+ logical: col.logical || { name: col.id || 'unknown', type: 'string' },
38
+ physical: col.physical || { name: '', type: '' }
39
+ }));
40
+
33
41
  return {
34
42
  ...table,
35
43
  id: table.id || 'unknown',
36
44
  name: table.name || table.id || 'Unnamed Table',
37
- columns: Array.isArray(table.columns) ? table.columns : [],
45
+ columns,
38
46
  sampleData: Array.isArray(sampleData) ? sampleData : []
39
47
  };
40
48
  });
@@ -47,7 +55,31 @@ function normalizeSchema(data) {
47
55
  * Spaces and hyphens are replaced with underscores.
48
56
  */
49
57
  function sanitize(str) {
50
- return str.replace(/[^a-zA-Z0-9_]/g, '_');
58
+ if (!str) return 'unknown';
59
+ // Mermaid ER/Flowchart labels can be wrapped in quotes for better compatibility
60
+ return str.replace(/[^a-zA-Z0-9_\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uff00-\uff9f\u4e00-\u9faf]/g, '_');
61
+ }
62
+
63
+ /**
64
+ * Generates Mermaid Data Lineage syntax.
65
+ */
66
+ function generateMermaidLineage(schema) {
67
+ let mermaid = 'graph TD\n';
68
+ let hasLineage = false;
69
+
70
+ schema.tables.forEach(table => {
71
+ if (table.lineage?.upstream && table.lineage.upstream.length > 0) {
72
+ hasLineage = true;
73
+ const targetName = sanitize(table.name);
74
+ table.lineage.upstream.forEach(upId => {
75
+ const sourceTable = schema.tables.find(t => t.id === upId);
76
+ const sourceName = sanitize(sourceTable?.name || upId);
77
+ mermaid += ` ${sourceName} --> ${targetName}\n`;
78
+ });
79
+ }
80
+ });
81
+
82
+ return hasLineage ? mermaid : null;
51
83
  }
52
84
 
53
85
  /**
@@ -88,6 +120,24 @@ function generateMermaidER(schema) {
88
120
  return mermaid;
89
121
  }
90
122
 
123
+ /**
124
+ * Generates the Sticky Notes section.
125
+ */
126
+ function generateNotesSection(schema) {
127
+ if (!schema.annotations || schema.annotations.length === 0) return null;
128
+
129
+ let md = '## Sticky Notes\n\n';
130
+ schema.annotations.forEach(note => {
131
+ md += `> **[Note]** ${note.text}\n`;
132
+ if (note.targetId) {
133
+ md += `> *Attached to: ${note.targetId} (${note.targetType})*\n`;
134
+ }
135
+ md += '\n';
136
+ });
137
+
138
+ return md;
139
+ }
140
+
91
141
  /**
92
142
  * Generates the full Markdown document.
93
143
  */
@@ -95,10 +145,21 @@ export function generateMarkdown(schema, modelName) {
95
145
  let md = `# ${modelName} Data Model Definition\n\n`;
96
146
 
97
147
  // Mermaid ER Diagram
98
- md += '## ER Diagram\n\n';
99
- md += '```mermaid\n';
100
- md += generateMermaidER(schema);
101
- md += '```\n\n';
148
+ if (schema.relationships && schema.relationships.length > 0) {
149
+ md += '## ER Diagram\n\n';
150
+ md += '```mermaid\n';
151
+ md += generateMermaidER(schema);
152
+ md += '```\n\n';
153
+ }
154
+
155
+ // Mermaid Data Lineage
156
+ const lineageMermaid = generateMermaidLineage(schema);
157
+ if (lineageMermaid) {
158
+ md += '## Data Lineage\n\n';
159
+ md += '```mermaid\n';
160
+ md += lineageMermaid;
161
+ md += '```\n\n';
162
+ }
102
163
 
103
164
  // Domains
104
165
  if (schema.domains && schema.domains.length > 0) {
@@ -123,20 +184,26 @@ export function generateMarkdown(schema, modelName) {
123
184
  md += `**Description**: ${table.conceptual.description}\n\n`;
124
185
  }
125
186
  if (table.appearance?.type) {
126
- md += `**Type**: ${table.appearance.type}\n\n`;
187
+ md += `**Type**: ${table.appearance.type.toUpperCase()}\n\n`;
127
188
  }
128
189
 
129
- // Columns Table
190
+ // Columns Table (Enhanced with Physical info)
130
191
  if (table.columns && table.columns.length > 0) {
131
192
  md += '#### Column Definitions\n\n';
132
- md += '| ID | Logical Name | Type | Key | Description |\n';
133
- md += '| --- | --- | --- | --- | --- |\n';
193
+ md += '| Logical Name | Physical Name | Logical Type | Physical Type | Key | Description |\n';
194
+ md += '| --- | --- | --- | --- | --- | --- |\n';
134
195
  table.columns.forEach(col => {
135
196
  const key = [
136
197
  col.logical?.isPrimaryKey ? 'PK' : '',
137
198
  col.logical?.isForeignKey ? 'FK' : ''
138
199
  ].filter(Boolean).join(', ');
139
- md += `| ${col.id} | ${col.logical?.name || '-'} | ${col.logical?.type || '-'} | ${key || '-'} | ${col.logical?.description || '-'} |\n`;
200
+ const logicalName = col.logical?.name || col.id;
201
+ const physicalName = col.physical?.name || '-';
202
+ const logicalType = col.logical?.type || '-';
203
+ const physicalType = col.physical?.type || '-';
204
+ const description = col.logical?.description || '-';
205
+
206
+ md += `| ${logicalName} | ${physicalName} | ${logicalType} | ${physicalType} | ${key || '-'} | ${description} |\n`;
140
207
  });
141
208
  md += '\n';
142
209
  }
@@ -157,6 +224,12 @@ export function generateMarkdown(schema, modelName) {
157
224
  }
158
225
  });
159
226
 
227
+ // Sticky Notes Section
228
+ const notesSection = generateNotesSection(schema);
229
+ if (notesSection) {
230
+ md += notesSection;
231
+ }
232
+
160
233
  return md;
161
234
  }
162
235
 
package/src/index.js CHANGED
@@ -16,7 +16,7 @@ const program = new Command();
16
16
  program
17
17
  .name('modscape')
18
18
  .description('Modscape: A YAML-driven data modeling visualizer CLI')
19
- .version('1.0.8');
19
+ .version('1.0.9');
20
20
 
21
21
  program
22
22
  .command('init')
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "visualizer",
3
3
  "private": true,
4
- "version": "1.0.8",
4
+ "version": "1.1.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -20,6 +20,7 @@
20
20
  "clsx": "^2.1.1",
21
21
  "codemirror": "^6.0.2",
22
22
  "dagre": "^0.8.5",
23
+ "html-to-image": "^1.11.13",
23
24
  "js-yaml": "^4.1.1",
24
25
  "lucide-react": "^0.575.0",
25
26
  "react": "^19.2.0",