@mailmodo/cli 0.0.30-beta.pr32.53 → 0.0.30
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/commands/deploy/index.js +1 -2
- package/dist/commands/edit/index.d.ts +1 -11
- package/dist/commands/edit/index.js +86 -148
- package/dist/commands/emails/index.d.ts +0 -2
- package/dist/commands/emails/index.js +1 -50
- package/dist/commands/init/index.js +1 -1
- package/dist/commands/preview/index.d.ts +2 -6
- package/dist/commands/preview/index.js +12 -38
- package/dist/lib/yaml-config.d.ts +0 -2
- package/dist/lib/yaml-config.js +0 -7
- package/oclif.manifest.json +40 -40
- package/package.json +1 -1
|
@@ -62,8 +62,7 @@ export default class Deploy extends BaseCommand {
|
|
|
62
62
|
async buildDeployPayload(yamlConfig) {
|
|
63
63
|
const emailsWithHtml = await Promise.all(yamlConfig.emails.map(async (email) => {
|
|
64
64
|
const html = (await loadTemplate(`${email.id}.html`)) || '';
|
|
65
|
-
|
|
66
|
-
return { ...this.mapEmailToPayload(email), html, plainHtml };
|
|
65
|
+
return { ...this.mapEmailToPayload(email), html, plainHtml: html };
|
|
67
66
|
}));
|
|
68
67
|
return {
|
|
69
68
|
...this.buildProjectPayload(yamlConfig.project),
|
|
@@ -10,17 +10,6 @@ export default class Edit extends BaseCommand {
|
|
|
10
10
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
12
|
};
|
|
13
|
-
run(): Promise<void>;
|
|
14
|
-
private runEditStep;
|
|
15
|
-
private handleUserAction;
|
|
16
|
-
private callEditApi;
|
|
17
|
-
private finalizeEdit;
|
|
18
|
-
private applyEmailChanges;
|
|
19
|
-
private persistChanges;
|
|
20
|
-
private logJsonResult;
|
|
21
|
-
private handleAcceptOutput;
|
|
22
|
-
private promptEditAction;
|
|
23
|
-
private askChangeDescription;
|
|
24
13
|
private showFieldDiff;
|
|
25
14
|
private stripHtml;
|
|
26
15
|
private truncate;
|
|
@@ -31,4 +20,5 @@ export default class Edit extends BaseCommand {
|
|
|
31
20
|
private showUnchanged;
|
|
32
21
|
private buildDiffPreview;
|
|
33
22
|
private showChangeSummary;
|
|
23
|
+
run(): Promise<void>;
|
|
34
24
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
|
-
import { confirm, input
|
|
2
|
+
import { confirm, input } from '@inquirer/prompts';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
5
5
|
import { API_ENDPOINTS } from '../../lib/constants.js';
|
|
6
|
-
import { loadTemplate,
|
|
6
|
+
import { loadTemplate, saveTemplate, saveYaml, } from '../../lib/yaml-config.js';
|
|
7
7
|
export default class Edit extends BaseCommand {
|
|
8
8
|
static args = {
|
|
9
9
|
id: Args.string({
|
|
@@ -22,152 +22,6 @@ export default class Edit extends BaseCommand {
|
|
|
22
22
|
description: 'Natural language description of the change',
|
|
23
23
|
}),
|
|
24
24
|
};
|
|
25
|
-
async run() {
|
|
26
|
-
const { args, flags } = await this.parse(Edit);
|
|
27
|
-
await this.ensureAuth();
|
|
28
|
-
const yamlConfig = await this.ensureYaml();
|
|
29
|
-
const emailIndex = yamlConfig.emails.findIndex((e) => e.id === args.id);
|
|
30
|
-
if (emailIndex === -1) {
|
|
31
|
-
this.error(`Email '${args.id}' not found in mailmodo.yaml.`);
|
|
32
|
-
}
|
|
33
|
-
const email = yamlConfig.emails[emailIndex];
|
|
34
|
-
const templateFilename = getTemplateFilename(email.id, email.style, yamlConfig.project?.emailStyle);
|
|
35
|
-
const ctx = {
|
|
36
|
-
email,
|
|
37
|
-
emailIndex,
|
|
38
|
-
templateFilename,
|
|
39
|
-
templateHtml: await loadTemplate(templateFilename),
|
|
40
|
-
yamlConfig,
|
|
41
|
-
};
|
|
42
|
-
const editFlags = {
|
|
43
|
-
json: flags.json ?? false,
|
|
44
|
-
yes: flags.yes ?? false,
|
|
45
|
-
};
|
|
46
|
-
const initialChange = flags.change?.trim() || (await this.askChangeDescription());
|
|
47
|
-
await this.runEditStep(ctx, initialChange, editFlags);
|
|
48
|
-
}
|
|
49
|
-
async runEditStep(ctx, changeDescription, flags) {
|
|
50
|
-
const response = await this.withApiSpinner({ json: flags.json, text: ' Applying AI edits...' }, () => this.callEditApi(changeDescription, ctx.email, ctx.templateHtml));
|
|
51
|
-
if (!response.ok) {
|
|
52
|
-
this.handleApiError(response);
|
|
53
|
-
}
|
|
54
|
-
const updated = response.data;
|
|
55
|
-
if (flags.json) {
|
|
56
|
-
this.log(JSON.stringify(this.buildDiffPreview(ctx.email, updated, ctx.templateHtml), null, 2));
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
this.showChangeSummary(ctx.email, updated, ctx.templateHtml);
|
|
60
|
-
}
|
|
61
|
-
if (flags.yes) {
|
|
62
|
-
await this.finalizeEdit(ctx, updated, flags);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
await this.handleUserAction(ctx, updated, changeDescription, flags);
|
|
66
|
-
}
|
|
67
|
-
async handleUserAction(ctx, updated, changeDescription, flags) {
|
|
68
|
-
this.log('');
|
|
69
|
-
const action = await this.promptEditAction();
|
|
70
|
-
if (action === 'skip') {
|
|
71
|
-
this.log('\n Changes discarded.\n');
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
if (action === 'retry') {
|
|
75
|
-
const newChange = await this.askChangeDescription();
|
|
76
|
-
await this.runEditStep(ctx, newChange, flags);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
await this.finalizeEdit(ctx, updated, flags);
|
|
80
|
-
}
|
|
81
|
-
callEditApi(changeDescription, email, templateHtml) {
|
|
82
|
-
return this.apiClient.post(API_ENDPOINTS.EDIT, {
|
|
83
|
-
changeRequest: changeDescription,
|
|
84
|
-
currentEmail: {
|
|
85
|
-
condition: email.condition,
|
|
86
|
-
goal: email.goal,
|
|
87
|
-
html: templateHtml,
|
|
88
|
-
id: email.id,
|
|
89
|
-
previewText: email.previewText,
|
|
90
|
-
subject: email.subject,
|
|
91
|
-
trigger: email.trigger,
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
async finalizeEdit(ctx, updated, flags) {
|
|
96
|
-
const oldSubject = ctx.email.subject;
|
|
97
|
-
this.applyEmailChanges(ctx.email, updated);
|
|
98
|
-
await this.persistChanges(ctx, updated);
|
|
99
|
-
if (flags.json) {
|
|
100
|
-
this.logJsonResult(ctx.email, updated, oldSubject);
|
|
101
|
-
}
|
|
102
|
-
else if (flags.yes) {
|
|
103
|
-
this.log(`\n Updated ${chalk.green('mailmodo.yaml')}\n`);
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
await this.handleAcceptOutput(ctx.email);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
applyEmailChanges(email, updated) {
|
|
110
|
-
if (updated.subject)
|
|
111
|
-
email.subject = updated.subject;
|
|
112
|
-
if (updated.previewText)
|
|
113
|
-
email.previewText = updated.previewText;
|
|
114
|
-
if (updated.ctaText)
|
|
115
|
-
email.ctaText = updated.ctaText;
|
|
116
|
-
}
|
|
117
|
-
async persistChanges(ctx, updated) {
|
|
118
|
-
const updatedYaml = {
|
|
119
|
-
...ctx.yamlConfig,
|
|
120
|
-
emails: [...ctx.yamlConfig.emails],
|
|
121
|
-
};
|
|
122
|
-
updatedYaml.emails[ctx.emailIndex] = ctx.email;
|
|
123
|
-
await saveYaml(updatedYaml);
|
|
124
|
-
if (updated.html) {
|
|
125
|
-
await saveTemplate(ctx.templateFilename, updated.html);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
logJsonResult(email, updated, oldSubject) {
|
|
129
|
-
this.log(JSON.stringify({
|
|
130
|
-
diff: {
|
|
131
|
-
previewText: updated.previewText && updated.previewText !== email.previewText
|
|
132
|
-
? { new: updated.previewText, old: email.previewText }
|
|
133
|
-
: undefined,
|
|
134
|
-
subject: oldSubject === email.subject
|
|
135
|
-
? undefined
|
|
136
|
-
: { new: email.subject, old: oldSubject },
|
|
137
|
-
},
|
|
138
|
-
email,
|
|
139
|
-
status: 'updated',
|
|
140
|
-
}, null, 2));
|
|
141
|
-
}
|
|
142
|
-
async handleAcceptOutput(email) {
|
|
143
|
-
this.log(`\n Updated ${chalk.green('mailmodo.yaml')}`);
|
|
144
|
-
const shouldPreview = await confirm({
|
|
145
|
-
default: true,
|
|
146
|
-
message: 'Preview the change?',
|
|
147
|
-
});
|
|
148
|
-
if (shouldPreview) {
|
|
149
|
-
await this.config.runCommand('preview', [email.id]);
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
this.log(` Run: ${chalk.cyan(`mailmodo preview ${email.id}`)}\n`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
async promptEditAction() {
|
|
156
|
-
return select({
|
|
157
|
-
message: 'Accept, try again, or skip?',
|
|
158
|
-
choices: [
|
|
159
|
-
{ name: 'Accept', value: 'accept' },
|
|
160
|
-
{ name: 'Try again', value: 'retry' },
|
|
161
|
-
{ name: 'Skip', value: 'skip' },
|
|
162
|
-
],
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
async askChangeDescription() {
|
|
166
|
-
return input({
|
|
167
|
-
message: 'What do you want to change?',
|
|
168
|
-
validate: (value) => value?.trim() ? true : 'Please describe the change',
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
25
|
showFieldDiff(label, oldVal, newVal) {
|
|
172
26
|
if (!newVal || oldVal === newVal)
|
|
173
27
|
return false;
|
|
@@ -285,4 +139,88 @@ export default class Edit extends BaseCommand {
|
|
|
285
139
|
this.showSuggestedChanges(email, updated, templateHtml, changed);
|
|
286
140
|
this.showUnchanged(email, templateHtml, changed);
|
|
287
141
|
}
|
|
142
|
+
async run() {
|
|
143
|
+
const { args, flags } = await this.parse(Edit);
|
|
144
|
+
await this.ensureAuth();
|
|
145
|
+
const yamlConfig = await this.ensureYaml();
|
|
146
|
+
const emailIndex = yamlConfig.emails.findIndex((e) => e.id === args.id);
|
|
147
|
+
if (emailIndex === -1) {
|
|
148
|
+
this.error(`Email '${args.id}' not found in mailmodo.yaml.`);
|
|
149
|
+
}
|
|
150
|
+
const email = yamlConfig.emails[emailIndex];
|
|
151
|
+
const templateHtml = await loadTemplate(`${email.id}.html`);
|
|
152
|
+
let changeDescription = flags.change;
|
|
153
|
+
if (!changeDescription) {
|
|
154
|
+
changeDescription = await input({
|
|
155
|
+
message: 'What do you want to change?',
|
|
156
|
+
validate: (value) => value?.trim() ? true : 'Please describe the change',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
const response = await this.withApiSpinner({ json: flags.json, text: ' Applying AI edits...' }, () => this.apiClient.post(API_ENDPOINTS.EDIT, {
|
|
160
|
+
changeRequest: changeDescription,
|
|
161
|
+
currentEmail: {
|
|
162
|
+
condition: email.condition,
|
|
163
|
+
goal: email.goal,
|
|
164
|
+
html: templateHtml,
|
|
165
|
+
id: email.id,
|
|
166
|
+
previewText: email.previewText,
|
|
167
|
+
subject: email.subject,
|
|
168
|
+
trigger: email.trigger,
|
|
169
|
+
},
|
|
170
|
+
}));
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
this.handleApiError(response);
|
|
173
|
+
}
|
|
174
|
+
const updated = response.data;
|
|
175
|
+
if (flags.json) {
|
|
176
|
+
this.log(JSON.stringify(this.buildDiffPreview(email, updated, templateHtml), null, 2));
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
this.showChangeSummary(email, updated, templateHtml);
|
|
180
|
+
}
|
|
181
|
+
if (!flags.yes) {
|
|
182
|
+
const accepted = await confirm({
|
|
183
|
+
default: true,
|
|
184
|
+
message: 'Accept changes?',
|
|
185
|
+
});
|
|
186
|
+
if (!accepted) {
|
|
187
|
+
this.log('\n Changes discarded.\n');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const oldSubject = email.subject;
|
|
192
|
+
const newSubject = updated.subject || email.subject;
|
|
193
|
+
if (updated.subject)
|
|
194
|
+
email.subject = updated.subject;
|
|
195
|
+
if (updated.previewText)
|
|
196
|
+
email.previewText = updated.previewText;
|
|
197
|
+
if (updated.ctaText)
|
|
198
|
+
email.ctaText = updated.ctaText;
|
|
199
|
+
const updatedYaml = {
|
|
200
|
+
...yamlConfig,
|
|
201
|
+
emails: [...yamlConfig.emails],
|
|
202
|
+
};
|
|
203
|
+
updatedYaml.emails[emailIndex] = email;
|
|
204
|
+
await saveYaml(updatedYaml);
|
|
205
|
+
if (updated.html) {
|
|
206
|
+
await saveTemplate(`${email.id}.html`, updated.html);
|
|
207
|
+
}
|
|
208
|
+
if (flags.json) {
|
|
209
|
+
this.log(JSON.stringify({
|
|
210
|
+
diff: {
|
|
211
|
+
previewText: updated.previewText && updated.previewText !== email.previewText
|
|
212
|
+
? { new: updated.previewText, old: email.previewText }
|
|
213
|
+
: undefined,
|
|
214
|
+
subject: oldSubject === newSubject
|
|
215
|
+
? undefined
|
|
216
|
+
: { new: newSubject, old: oldSubject },
|
|
217
|
+
},
|
|
218
|
+
email,
|
|
219
|
+
status: 'updated',
|
|
220
|
+
}, null, 2));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
this.log(`\n Updated ${chalk.green('mailmodo.yaml')}`);
|
|
224
|
+
this.log(` Preview the change: ${chalk.cyan(`mailmodo preview ${email.id}`)}\n`);
|
|
225
|
+
}
|
|
288
226
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { confirm, input } from '@inquirer/prompts';
|
|
1
|
+
import { input } from '@inquirer/prompts';
|
|
4
2
|
import chalk from 'chalk';
|
|
5
|
-
import open from 'open';
|
|
6
3
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
7
4
|
export default class Emails extends BaseCommand {
|
|
8
5
|
static description = 'List and view configured email sequences';
|
|
@@ -64,53 +61,7 @@ export default class Emails extends BaseCommand {
|
|
|
64
61
|
this.log(` ${chalk.bold('Goal:')} ${email.goal}`);
|
|
65
62
|
}
|
|
66
63
|
this.log('');
|
|
67
|
-
const openIt = await confirm({
|
|
68
|
-
default: true,
|
|
69
|
-
message: 'Open template in editor?',
|
|
70
|
-
});
|
|
71
|
-
if (openIt) {
|
|
72
|
-
await this.openTemplateInEditor(email.template);
|
|
73
|
-
}
|
|
74
64
|
}
|
|
75
65
|
}
|
|
76
66
|
}
|
|
77
|
-
async openTemplateInEditor(template) {
|
|
78
|
-
const templatePath = join(process.cwd(), template);
|
|
79
|
-
this.log(`\n Opening ${template}...\n`);
|
|
80
|
-
const editor = process.env.VISUAL || process.env.EDITOR;
|
|
81
|
-
if (editor) {
|
|
82
|
-
const [cmd, ...editorArgs] = editor.trim().split(/\s+/);
|
|
83
|
-
const launched = await new Promise((resolve) => {
|
|
84
|
-
const child = spawn(cmd, [...editorArgs, templatePath], {
|
|
85
|
-
stdio: 'inherit',
|
|
86
|
-
});
|
|
87
|
-
child.on('error', () => resolve(false));
|
|
88
|
-
child.on('close', () => resolve(true));
|
|
89
|
-
});
|
|
90
|
-
if (launched)
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (await this.trySpawnEditor('code', templatePath))
|
|
94
|
-
return;
|
|
95
|
-
try {
|
|
96
|
-
await open(templatePath);
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
this.log(` ${chalk.dim(`Could not open editor. Open the file manually: ${templatePath}`)}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
trySpawnEditor(editor, filePath) {
|
|
103
|
-
const [cmd, args] = process.platform === 'win32'
|
|
104
|
-
? ['cmd.exe', ['/c', editor, filePath]]
|
|
105
|
-
: [editor, [filePath]];
|
|
106
|
-
return new Promise((resolve) => {
|
|
107
|
-
const child = spawn(cmd, [...args], { stdio: 'ignore' });
|
|
108
|
-
child.on('error', () => {
|
|
109
|
-
resolve(false);
|
|
110
|
-
});
|
|
111
|
-
child.on('close', (code) => {
|
|
112
|
-
resolve(code === 0);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
67
|
}
|
|
@@ -139,7 +139,7 @@ export default class Init extends BaseCommand {
|
|
|
139
139
|
project: {
|
|
140
140
|
brandColor: analysisPayload.brand?.color || DEFAULT_BRAND_COLOR,
|
|
141
141
|
description: analysisPayload.description,
|
|
142
|
-
emailStyle: '
|
|
142
|
+
emailStyle: 'branded',
|
|
143
143
|
fromEmail: '',
|
|
144
144
|
fromName: `Team ${analysisPayload.productName}`,
|
|
145
145
|
logoUrl: analysisPayload.brand?.logoUrl || '',
|
|
@@ -23,12 +23,8 @@ export default class Preview extends BaseCommand {
|
|
|
23
23
|
*/
|
|
24
24
|
private sendTestEmail;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
private findAvailablePort;
|
|
29
|
-
/**
|
|
30
|
-
* Starts a local HTTP server to serve the rendered email template,
|
|
31
|
-
* then opens the user's default browser to view it.
|
|
26
|
+
* Starts a local HTTP server on PREVIEW_PORT to serve the rendered email
|
|
27
|
+
* template, then opens the user's default browser to view it.
|
|
32
28
|
*/
|
|
33
29
|
private startPreviewServer;
|
|
34
30
|
}
|
|
@@ -4,7 +4,7 @@ import chalk from 'chalk';
|
|
|
4
4
|
import open from 'open';
|
|
5
5
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
6
6
|
import { API_ENDPOINTS, PREVIEW_PORT } from '../../lib/constants.js';
|
|
7
|
-
import { loadTemplate
|
|
7
|
+
import { loadTemplate } from '../../lib/yaml-config.js';
|
|
8
8
|
/* eslint-disable camelcase */
|
|
9
9
|
const SAMPLE_DATA = Object.freeze({
|
|
10
10
|
app_url: 'https://yourapp.com',
|
|
@@ -84,8 +84,7 @@ export default class Preview extends BaseCommand {
|
|
|
84
84
|
app_url: yamlConfig.project?.url || 'https://yourapp.com', // eslint-disable-line camelcase
|
|
85
85
|
product_name: yamlConfig.project?.name || 'YourApp', // eslint-disable-line camelcase
|
|
86
86
|
};
|
|
87
|
-
const
|
|
88
|
-
const templateHtml = await loadTemplate(getTemplateFilename(email.id, email.style, yamlConfig.project?.emailStyle));
|
|
87
|
+
const templateHtml = await loadTemplate(`${email.id}.html`);
|
|
89
88
|
if (flags.send) {
|
|
90
89
|
const rendered = templateHtml
|
|
91
90
|
? renderTemplate(templateHtml, sampleData)
|
|
@@ -101,10 +100,7 @@ export default class Preview extends BaseCommand {
|
|
|
101
100
|
await this.renderText(email, templateHtml, sampleData, flags.json);
|
|
102
101
|
return;
|
|
103
102
|
}
|
|
104
|
-
await this.startPreviewServer(email, templateHtml, sampleData,
|
|
105
|
-
effectiveStyle,
|
|
106
|
-
jsonOutput: flags.json,
|
|
107
|
-
});
|
|
103
|
+
await this.startPreviewServer(email, templateHtml, sampleData, flags.json);
|
|
108
104
|
}
|
|
109
105
|
/**
|
|
110
106
|
* Renders a plain text version of the email to stdout. Used by AI agents
|
|
@@ -158,28 +154,10 @@ export default class Preview extends BaseCommand {
|
|
|
158
154
|
this.log('');
|
|
159
155
|
}
|
|
160
156
|
/**
|
|
161
|
-
*
|
|
157
|
+
* Starts a local HTTP server on PREVIEW_PORT to serve the rendered email
|
|
158
|
+
* template, then opens the user's default browser to view it.
|
|
162
159
|
*/
|
|
163
|
-
async
|
|
164
|
-
if (startPort > endPort) {
|
|
165
|
-
throw new Error(`No available port found starting from port ${endPort - 10}`);
|
|
166
|
-
}
|
|
167
|
-
const available = await new Promise((resolve) => {
|
|
168
|
-
const probe = createServer();
|
|
169
|
-
probe.once('error', () => resolve(false));
|
|
170
|
-
probe.once('listening', () => probe.close(() => resolve(true)));
|
|
171
|
-
probe.listen(startPort);
|
|
172
|
-
});
|
|
173
|
-
return available
|
|
174
|
-
? startPort
|
|
175
|
-
: this.findAvailablePort(startPort + 1, endPort);
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Starts a local HTTP server to serve the rendered email template,
|
|
179
|
-
* then opens the user's default browser to view it.
|
|
180
|
-
*/
|
|
181
|
-
async startPreviewServer(email, templateHtml, sampleData, opts) {
|
|
182
|
-
const { effectiveStyle, jsonOutput } = opts;
|
|
160
|
+
async startPreviewServer(email, templateHtml, sampleData, jsonOutput) {
|
|
183
161
|
const rendered = templateHtml
|
|
184
162
|
? renderTemplate(templateHtml, sampleData)
|
|
185
163
|
: '<p>No template found.</p>';
|
|
@@ -205,7 +183,7 @@ export default class Preview extends BaseCommand {
|
|
|
205
183
|
<body>
|
|
206
184
|
<div class="preview-bar">
|
|
207
185
|
<h3>Mailmodo Preview — ${email.id}</h3>
|
|
208
|
-
<span>Style: ${
|
|
186
|
+
<span>Style: ${email.style || 'branded'} · Press Ctrl+C in terminal to stop</span>
|
|
209
187
|
</div>
|
|
210
188
|
<div class="email-frame">
|
|
211
189
|
<div class="email-header">
|
|
@@ -216,15 +194,11 @@ export default class Preview extends BaseCommand {
|
|
|
216
194
|
</div>
|
|
217
195
|
</body>
|
|
218
196
|
</html>`;
|
|
219
|
-
const port = await this.findAvailablePort(PREVIEW_PORT);
|
|
220
|
-
if (!jsonOutput && port !== PREVIEW_PORT) {
|
|
221
|
-
this.log(`\n ${chalk.yellow('!')} Port ${PREVIEW_PORT} is already in use. Opening preview on port ${chalk.cyan(String(port))}.`);
|
|
222
|
-
}
|
|
223
197
|
if (jsonOutput) {
|
|
224
198
|
this.log(JSON.stringify({
|
|
225
199
|
id: email.id,
|
|
226
|
-
style:
|
|
227
|
-
url: `http://localhost:${
|
|
200
|
+
style: email.style || 'branded',
|
|
201
|
+
url: `http://localhost:${PREVIEW_PORT}`,
|
|
228
202
|
}, null, 2));
|
|
229
203
|
}
|
|
230
204
|
const server = createServer((_req, res) => {
|
|
@@ -232,11 +206,11 @@ export default class Preview extends BaseCommand {
|
|
|
232
206
|
res.end(wrapperHtml);
|
|
233
207
|
});
|
|
234
208
|
await new Promise((resolve) => {
|
|
235
|
-
server.listen(
|
|
209
|
+
server.listen(PREVIEW_PORT, () => resolve());
|
|
236
210
|
});
|
|
237
|
-
const url = `http://localhost:${
|
|
211
|
+
const url = `http://localhost:${PREVIEW_PORT}`;
|
|
238
212
|
if (!jsonOutput) {
|
|
239
|
-
this.log(`\n Style: ${chalk.cyan(
|
|
213
|
+
this.log(`\n Style: ${chalk.cyan(email.style || 'branded')}`);
|
|
240
214
|
this.log(` Preview server at ${chalk.cyan(url)}`);
|
|
241
215
|
this.log(` Opening in browser...\n`);
|
|
242
216
|
this.log(` ${chalk.dim('Press Ctrl+C to stop the preview server.')}\n`);
|
|
@@ -68,5 +68,3 @@ export declare function saveTemplate(filename: string, html: string, cwd?: strin
|
|
|
68
68
|
* or null if the file doesn't exist or can't be read.
|
|
69
69
|
*/
|
|
70
70
|
export declare function loadTemplate(filename: string, cwd?: string): Promise<null | string>;
|
|
71
|
-
export declare function getEmailStyle(emailStyle?: 'branded' | 'plain', projectStyle?: 'branded' | 'plain'): 'branded' | 'plain';
|
|
72
|
-
export declare function getTemplateFilename(emailId: string, emailStyle?: 'branded' | 'plain', projectStyle?: 'branded' | 'plain'): string;
|
package/dist/lib/yaml-config.js
CHANGED
|
@@ -72,10 +72,3 @@ export async function loadTemplate(filename, cwd) {
|
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
export function getEmailStyle(emailStyle, projectStyle) {
|
|
76
|
-
return emailStyle ?? projectStyle ?? 'branded';
|
|
77
|
-
}
|
|
78
|
-
export function getTemplateFilename(emailId, emailStyle, projectStyle) {
|
|
79
|
-
const style = getEmailStyle(emailStyle, projectStyle);
|
|
80
|
-
return style === 'plain' ? `${emailId}_plain.html` : `${emailId}.html`;
|
|
81
|
-
}
|
package/oclif.manifest.json
CHANGED
|
@@ -365,45 +365,6 @@
|
|
|
365
365
|
"index.js"
|
|
366
366
|
]
|
|
367
367
|
},
|
|
368
|
-
"login": {
|
|
369
|
-
"aliases": [],
|
|
370
|
-
"args": {},
|
|
371
|
-
"description": "Authenticate with Mailmodo using your API key",
|
|
372
|
-
"examples": [
|
|
373
|
-
"<%= config.bin %> login",
|
|
374
|
-
"MAILMODO_API_KEY=mm_live_xxx <%= config.bin %> login"
|
|
375
|
-
],
|
|
376
|
-
"flags": {
|
|
377
|
-
"json": {
|
|
378
|
-
"description": "Output as JSON",
|
|
379
|
-
"name": "json",
|
|
380
|
-
"allowNo": false,
|
|
381
|
-
"type": "boolean"
|
|
382
|
-
},
|
|
383
|
-
"yes": {
|
|
384
|
-
"char": "y",
|
|
385
|
-
"description": "Skip confirmation prompts",
|
|
386
|
-
"name": "yes",
|
|
387
|
-
"allowNo": false,
|
|
388
|
-
"type": "boolean"
|
|
389
|
-
}
|
|
390
|
-
},
|
|
391
|
-
"hasDynamicHelp": false,
|
|
392
|
-
"hiddenAliases": [],
|
|
393
|
-
"id": "login",
|
|
394
|
-
"pluginAlias": "@mailmodo/cli",
|
|
395
|
-
"pluginName": "@mailmodo/cli",
|
|
396
|
-
"pluginType": "core",
|
|
397
|
-
"strict": true,
|
|
398
|
-
"enableJsonFlag": false,
|
|
399
|
-
"isESM": true,
|
|
400
|
-
"relativePath": [
|
|
401
|
-
"dist",
|
|
402
|
-
"commands",
|
|
403
|
-
"login",
|
|
404
|
-
"index.js"
|
|
405
|
-
]
|
|
406
|
-
},
|
|
407
368
|
"logout": {
|
|
408
369
|
"aliases": [],
|
|
409
370
|
"args": {},
|
|
@@ -655,7 +616,46 @@
|
|
|
655
616
|
"status",
|
|
656
617
|
"index.js"
|
|
657
618
|
]
|
|
619
|
+
},
|
|
620
|
+
"login": {
|
|
621
|
+
"aliases": [],
|
|
622
|
+
"args": {},
|
|
623
|
+
"description": "Authenticate with Mailmodo using your API key",
|
|
624
|
+
"examples": [
|
|
625
|
+
"<%= config.bin %> login",
|
|
626
|
+
"MAILMODO_API_KEY=mm_live_xxx <%= config.bin %> login"
|
|
627
|
+
],
|
|
628
|
+
"flags": {
|
|
629
|
+
"json": {
|
|
630
|
+
"description": "Output as JSON",
|
|
631
|
+
"name": "json",
|
|
632
|
+
"allowNo": false,
|
|
633
|
+
"type": "boolean"
|
|
634
|
+
},
|
|
635
|
+
"yes": {
|
|
636
|
+
"char": "y",
|
|
637
|
+
"description": "Skip confirmation prompts",
|
|
638
|
+
"name": "yes",
|
|
639
|
+
"allowNo": false,
|
|
640
|
+
"type": "boolean"
|
|
641
|
+
}
|
|
642
|
+
},
|
|
643
|
+
"hasDynamicHelp": false,
|
|
644
|
+
"hiddenAliases": [],
|
|
645
|
+
"id": "login",
|
|
646
|
+
"pluginAlias": "@mailmodo/cli",
|
|
647
|
+
"pluginName": "@mailmodo/cli",
|
|
648
|
+
"pluginType": "core",
|
|
649
|
+
"strict": true,
|
|
650
|
+
"enableJsonFlag": false,
|
|
651
|
+
"isESM": true,
|
|
652
|
+
"relativePath": [
|
|
653
|
+
"dist",
|
|
654
|
+
"commands",
|
|
655
|
+
"login",
|
|
656
|
+
"index.js"
|
|
657
|
+
]
|
|
658
658
|
}
|
|
659
659
|
},
|
|
660
|
-
"version": "0.0.30
|
|
660
|
+
"version": "0.0.30"
|
|
661
661
|
}
|