tiendu 0.8.1 → 0.9.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/README.md +37 -14
- package/bin/tiendu.js +65 -21
- package/lib/build.mjs +7 -7
- package/lib/config.mjs +63 -1
- package/lib/dev.mjs +11 -25
- package/lib/init.mjs +184 -15
- package/lib/preview.mjs +2 -0
- package/lib/publish.mjs +2 -2
- package/lib/pull.mjs +64 -2
- package/lib/push.mjs +9 -6
- package/lib/stores.mjs +7 -2
- package/package.json +1 -1
- package/lib/local-preview.mjs +0 -393
package/lib/init.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { mkdir,
|
|
1
|
+
import { mkdir, readdir } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { readConfig, readCredentials, writeConfig, writeCredentials } from "./config.mjs";
|
|
4
|
-
import { fetchUserStores } from "./api.mjs";
|
|
4
|
+
import { fetchUserStores, fetchPreview } from "./api.mjs";
|
|
5
5
|
import { formatInitSummary } from "./stores.mjs";
|
|
6
|
+
import { listPreviews, createPreview, getPreviewDisplayName, getPreviewUrl } from "./preview.mjs";
|
|
7
|
+
import { pull } from "./pull.mjs";
|
|
6
8
|
import * as ui from "./ui.mjs";
|
|
7
9
|
|
|
8
10
|
const DEFAULT_API_BASE_URL = "https://tiendu.uy";
|
|
@@ -21,19 +23,52 @@ const resolveBaseUrlOrFail = (baseUrlArg) => {
|
|
|
21
23
|
return candidate;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
26
|
+
const checkTargetDir = async (dirArg) => {
|
|
27
|
+
if (dirArg) {
|
|
28
|
+
const targetDir = path.resolve(process.cwd(), dirArg);
|
|
29
|
+
try {
|
|
30
|
+
const entries = await readdir(targetDir);
|
|
31
|
+
const hasContent = entries.filter((n) => !n.startsWith(".")).length > 0;
|
|
32
|
+
if (hasContent && !ui.isInteractive()) {
|
|
33
|
+
ui.log.error(`Directory "${dirArg}" already exists and is not empty.`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
if (hasContent) {
|
|
38
|
+
const confirmed = await ui.confirm({
|
|
39
|
+
message: `Directory "${dirArg}" already exists. Overwrite its contents?`,
|
|
40
|
+
});
|
|
41
|
+
if (ui.isCancel(confirmed) || !confirmed) {
|
|
42
|
+
ui.cancel("Setup cancelled.");
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
// Directory doesn't exist — fine
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
28
51
|
|
|
29
52
|
try {
|
|
30
|
-
await
|
|
31
|
-
|
|
32
|
-
|
|
53
|
+
const entries = await readdir(process.cwd());
|
|
54
|
+
const hasContent = entries.filter((n) => !n.startsWith(".") && n !== ".cli").length > 0;
|
|
55
|
+
if (hasContent && ui.isInteractive()) {
|
|
56
|
+
const confirmed = await ui.confirm({
|
|
57
|
+
message: "Current directory is not empty. Overwrite its contents?",
|
|
58
|
+
});
|
|
59
|
+
if (ui.isCancel(confirmed) || !confirmed) {
|
|
60
|
+
ui.cancel("Setup cancelled.");
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
33
64
|
} catch {
|
|
34
|
-
//
|
|
65
|
+
// Can't happen for cwd
|
|
35
66
|
}
|
|
67
|
+
};
|
|
36
68
|
|
|
69
|
+
const enterTargetDir = async (dirArg) => {
|
|
70
|
+
if (!dirArg) return;
|
|
71
|
+
const targetDir = path.resolve(process.cwd(), dirArg);
|
|
37
72
|
await mkdir(targetDir, { recursive: true });
|
|
38
73
|
process.chdir(targetDir);
|
|
39
74
|
};
|
|
@@ -108,9 +143,7 @@ const collectDirectInputs = (apiKeyArg, baseUrlArg) => {
|
|
|
108
143
|
};
|
|
109
144
|
};
|
|
110
145
|
|
|
111
|
-
export const init = async ({ dirArg, apiKeyArg, baseUrlArg } = {}) => {
|
|
112
|
-
await prepareWorkDir(dirArg);
|
|
113
|
-
|
|
146
|
+
export const init = async ({ dirArg, apiKeyArg, baseUrlArg, previewKeyArg } = {}) => {
|
|
114
147
|
const existingConfig = await readConfig();
|
|
115
148
|
const existingCredentials = await readCredentials();
|
|
116
149
|
const hasExistingSetup = Boolean(existingConfig || existingCredentials);
|
|
@@ -121,7 +154,7 @@ export const init = async ({ dirArg, apiKeyArg, baseUrlArg } = {}) => {
|
|
|
121
154
|
process.exit(1);
|
|
122
155
|
}
|
|
123
156
|
|
|
124
|
-
if (!directMode) {
|
|
157
|
+
if (!directMode && !dirArg) {
|
|
125
158
|
await ensureResetAllowed(hasExistingSetup);
|
|
126
159
|
}
|
|
127
160
|
|
|
@@ -146,20 +179,156 @@ export const init = async ({ dirArg, apiKeyArg, baseUrlArg } = {}) => {
|
|
|
146
179
|
process.exit(1);
|
|
147
180
|
}
|
|
148
181
|
|
|
149
|
-
|
|
150
|
-
|
|
182
|
+
let selectedStore = stores.length === 1 ? stores[0] : null;
|
|
183
|
+
|
|
184
|
+
if (!selectedStore && ui.isInteractive()) {
|
|
185
|
+
spinner.stop(`Connected to Tiendu. ${stores.length} stores available.`);
|
|
186
|
+
|
|
187
|
+
const storeOptions = stores.map((store) => ({
|
|
188
|
+
value: store.id,
|
|
189
|
+
label: store.name,
|
|
190
|
+
hint: `ID: ${store.id}`,
|
|
191
|
+
}));
|
|
192
|
+
|
|
193
|
+
const chosen = await ui.select({
|
|
194
|
+
message: "Select a store",
|
|
195
|
+
options: storeOptions,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (ui.isCancel(chosen)) {
|
|
199
|
+
ui.cancel("Setup cancelled.");
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
selectedStore = stores.find((s) => s.id === chosen) ?? null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (selectedStore || stores.length === 1) {
|
|
207
|
+
spinner.stop(
|
|
208
|
+
`Connected to Tiendu. ${stores.length} store${stores.length === 1 ? "" : "s"} available.`,
|
|
209
|
+
);
|
|
210
|
+
} else {
|
|
211
|
+
spinner.stop(
|
|
212
|
+
`Connected to Tiendu. ${stores.length} stores available.`,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
let previewKey = null;
|
|
217
|
+
|
|
218
|
+
if (previewKeyArg && selectedStore) {
|
|
219
|
+
const result = await fetchPreview(apiBaseUrl, apiKey, selectedStore.id, previewKeyArg);
|
|
220
|
+
if (result.ok) {
|
|
221
|
+
previewKey = previewKeyArg;
|
|
222
|
+
const url = getPreviewUrl(apiBaseUrl, result.data);
|
|
223
|
+
const displayName = getPreviewDisplayName(result.data);
|
|
224
|
+
ui.log.message(`Preview "${displayName}" (${previewKey})`);
|
|
225
|
+
ui.log.message(` ${url}`);
|
|
226
|
+
} else {
|
|
227
|
+
ui.log.error(`Preview ${previewKeyArg} not found.`);
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
} else if (selectedStore && ui.isInteractive()) {
|
|
231
|
+
const listResult = await listPreviews(apiBaseUrl, apiKey, selectedStore.id);
|
|
232
|
+
let previews = [];
|
|
233
|
+
if (listResult.ok) {
|
|
234
|
+
previews = listResult.data;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const LIVE_VALUE = "__live__";
|
|
238
|
+
const CREATE_NEW_VALUE = "__create_new__";
|
|
239
|
+
|
|
240
|
+
const options = [
|
|
241
|
+
{
|
|
242
|
+
value: LIVE_VALUE,
|
|
243
|
+
label: "Live theme",
|
|
244
|
+
hint: "No preview — work directly with the live storefront",
|
|
245
|
+
},
|
|
246
|
+
...previews.map((p) => ({
|
|
247
|
+
value: p.previewKey,
|
|
248
|
+
label: `${getPreviewDisplayName(p)} (${p.previewKey})`,
|
|
249
|
+
})),
|
|
250
|
+
{ value: CREATE_NEW_VALUE, label: "Create a new preview" },
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
const chosen = await ui.select({
|
|
254
|
+
message: "Select a preview",
|
|
255
|
+
options,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
if (ui.isCancel(chosen)) {
|
|
259
|
+
ui.cancel("Setup cancelled.");
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (chosen === LIVE_VALUE) {
|
|
264
|
+
// Live theme — no preview key
|
|
265
|
+
} else if (chosen === CREATE_NEW_VALUE) {
|
|
266
|
+
const nameInput = await ui.text({
|
|
267
|
+
message: "Preview name (optional)",
|
|
268
|
+
placeholder: "Press Enter to skip",
|
|
269
|
+
defaultValue: "",
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
if (ui.isCancel(nameInput)) {
|
|
273
|
+
ui.cancel("Setup cancelled.");
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const name = (nameInput ?? "").trim();
|
|
278
|
+
const createSpinner = ui.spinner();
|
|
279
|
+
createSpinner.start("Creating preview...");
|
|
280
|
+
|
|
281
|
+
const createResult = await createPreview(
|
|
282
|
+
apiBaseUrl,
|
|
283
|
+
apiKey,
|
|
284
|
+
selectedStore.id,
|
|
285
|
+
name,
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
if (!createResult.ok) {
|
|
289
|
+
createSpinner.stop("Failed to create preview.", 1);
|
|
290
|
+
ui.log.error(createResult.error);
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const preview = createResult.data;
|
|
295
|
+
previewKey = preview.previewKey;
|
|
296
|
+
const url = getPreviewUrl(apiBaseUrl, preview);
|
|
297
|
+
const displayName = getPreviewDisplayName(preview);
|
|
298
|
+
createSpinner.stop(`Preview "${displayName}" created (${previewKey})`);
|
|
299
|
+
ui.log.message(` ${url}`);
|
|
300
|
+
} else {
|
|
301
|
+
const selectedPreview = previews.find((p) => p.previewKey === chosen);
|
|
302
|
+
previewKey = chosen;
|
|
303
|
+
if (selectedPreview) {
|
|
304
|
+
const displayName = getPreviewDisplayName(selectedPreview);
|
|
305
|
+
const url = getPreviewUrl(apiBaseUrl, selectedPreview);
|
|
306
|
+
ui.log.message(`Preview "${displayName}" (${previewKey})`);
|
|
307
|
+
ui.log.message(` ${url}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
await checkTargetDir(dirArg);
|
|
313
|
+
await enterTargetDir(dirArg);
|
|
151
314
|
|
|
152
315
|
await writeCredentials({ apiKey });
|
|
153
316
|
await writeConfig({
|
|
154
317
|
apiBaseUrl,
|
|
155
318
|
...(selectedStore ? { storeId: selectedStore.id } : {}),
|
|
319
|
+
...(previewKey ? { previewKey } : {}),
|
|
156
320
|
});
|
|
157
321
|
|
|
322
|
+
if (selectedStore) {
|
|
323
|
+
await pull({ previewKey: previewKey || undefined, confirmSourceSync: false });
|
|
324
|
+
}
|
|
325
|
+
|
|
158
326
|
const summary = formatInitSummary({
|
|
159
327
|
apiBaseUrl,
|
|
160
328
|
usedDefaultBaseUrl,
|
|
161
329
|
stores,
|
|
162
330
|
selectedStore,
|
|
331
|
+
previewKey,
|
|
163
332
|
});
|
|
164
333
|
|
|
165
334
|
if (ui.isInteractive()) {
|
package/lib/preview.mjs
CHANGED
|
@@ -429,12 +429,14 @@ export const previewShow = async () => {
|
|
|
429
429
|
const preview = result.data;
|
|
430
430
|
const url = getPreviewUrl(config.apiBaseUrl, preview);
|
|
431
431
|
const displayName = getPreviewDisplayName(preview);
|
|
432
|
+
const editorUrl = `${config.apiBaseUrl}/admin/tiendas/${config.storeId}/tema/personalizar?preview=${preview.previewKey}`;
|
|
432
433
|
|
|
433
434
|
ui.note(
|
|
434
435
|
[
|
|
435
436
|
`Name: ${displayName}`,
|
|
436
437
|
`Key: ${preview.previewKey}`,
|
|
437
438
|
`URL: ${url}`,
|
|
439
|
+
`Editor: ${editorUrl}`,
|
|
438
440
|
`Created: ${formatRelativeDate(preview.createdAt)}`,
|
|
439
441
|
].join("\n"),
|
|
440
442
|
"Attached preview",
|
package/lib/publish.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { push } from "./push.mjs";
|
|
8
8
|
import * as ui from "./ui.mjs";
|
|
9
9
|
|
|
10
|
-
export const publish = async ({ skipBuild = false, previewKey: previewKeyArg,
|
|
10
|
+
export const publish = async ({ skipBuild = false, previewKey: previewKeyArg, overrideState = false } = {}) => {
|
|
11
11
|
const { config, credentials } = await loadConfigOrFail();
|
|
12
12
|
|
|
13
13
|
// Resolve preview key: explicit arg > interactive picker
|
|
@@ -42,7 +42,7 @@ export const publish = async ({ skipBuild = false, previewKey: previewKeyArg, sk
|
|
|
42
42
|
? "Syncing existing dist/ output to the preview before publishing..."
|
|
43
43
|
: "Building and syncing the latest dist/ output before publishing...",
|
|
44
44
|
);
|
|
45
|
-
await push({ skipBuild, previewKey,
|
|
45
|
+
await push({ skipBuild, previewKey, overrideState });
|
|
46
46
|
|
|
47
47
|
const spinner = ui.spinner();
|
|
48
48
|
spinner.start("Publishing preview to live storefront...");
|
package/lib/pull.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { mkdir, rm } from "node:fs/promises";
|
|
1
|
+
import { cp, mkdir, readdir, rm } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import { getDistDir, loadConfigOrFail } from "./config.mjs";
|
|
3
4
|
import { downloadStorefrontArchive, downloadPreviewArchive } from "./api.mjs";
|
|
4
5
|
import { fetchPreviewDetails } from "./preview.mjs";
|
|
@@ -12,8 +13,52 @@ const formatBytes = (bytes) => {
|
|
|
12
13
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
13
14
|
};
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
const syncDistToSrc = async (distDir) => {
|
|
17
|
+
const rootDir = path.resolve(distDir, "..");
|
|
18
|
+
const srcDir = path.join(rootDir, "src");
|
|
19
|
+
let entries;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
entries = await readdir(distDir, { withFileTypes: true });
|
|
23
|
+
} catch {
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const distDirs = entries.filter(
|
|
28
|
+
(entry) => entry.isDirectory() && !entry.name.startsWith("."),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
let synced = 0;
|
|
32
|
+
|
|
33
|
+
for (const dir of distDirs) {
|
|
34
|
+
const distSubDir = path.join(distDir, dir.name);
|
|
35
|
+
const srcDest = path.join(srcDir, dir.name);
|
|
36
|
+
const rootDest = path.join(rootDir, dir.name);
|
|
37
|
+
|
|
38
|
+
await rm(srcDest, { recursive: true, force: true });
|
|
39
|
+
|
|
40
|
+
if (srcDest !== rootDest) {
|
|
41
|
+
await rm(rootDest, { recursive: true, force: true });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await cp(distSubDir, srcDest, { recursive: true });
|
|
45
|
+
synced++;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return synced;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const pull = async ({ previewKey, forceLive = false, confirmSourceSync = true } = {}) => {
|
|
16
52
|
const { config, credentials } = await loadConfigOrFail();
|
|
53
|
+
|
|
54
|
+
if (!previewKey && !forceLive && config.previewKey) {
|
|
55
|
+
previewKey = config.previewKey;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (forceLive) {
|
|
59
|
+
previewKey = undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
17
62
|
const previewDetails = previewKey
|
|
18
63
|
? await fetchPreviewDetails(
|
|
19
64
|
config.apiBaseUrl,
|
|
@@ -63,6 +108,23 @@ export const pull = async ({ previewKey } = {}) => {
|
|
|
63
108
|
`${extractedFiles.length} file${extractedFiles.length === 1 ? "" : "s"} extracted${suffix}.`,
|
|
64
109
|
);
|
|
65
110
|
|
|
111
|
+
if (confirmSourceSync && ui.isInteractive()) {
|
|
112
|
+
const confirmed = await ui.confirm({
|
|
113
|
+
message: "Sync downloaded theme directories to src/? This overwrites local theme files.",
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (ui.isCancel(confirmed) || !confirmed) {
|
|
117
|
+
ui.cancel("Source sync cancelled. dist/ was updated.");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
spinner.start("Syncing dist to src...");
|
|
123
|
+
const syncedDirs = await syncDistToSrc(outputDir);
|
|
124
|
+
spinner.stop(
|
|
125
|
+
`${syncedDirs} director${syncedDirs === 1 ? "y" : "ies"} synced to src/.`,
|
|
126
|
+
);
|
|
127
|
+
|
|
66
128
|
if (previewDetails?.ok) {
|
|
67
129
|
ui.log.message(` ${previewDetails.data.url}`);
|
|
68
130
|
}
|
package/lib/push.mjs
CHANGED
|
@@ -26,11 +26,11 @@ export const pushPreparedDirectoryToPreview = async ({
|
|
|
26
26
|
compressMessage = "Compressing files...",
|
|
27
27
|
uploadMessage,
|
|
28
28
|
retryMessage,
|
|
29
|
-
|
|
29
|
+
overrideState = false,
|
|
30
30
|
}) => {
|
|
31
31
|
spinner.message(compressMessage);
|
|
32
32
|
|
|
33
|
-
const shouldInclude =
|
|
33
|
+
const shouldInclude = !overrideState
|
|
34
34
|
? (relativePath) => !isInstanceFile(relativePath)
|
|
35
35
|
: undefined;
|
|
36
36
|
|
|
@@ -40,7 +40,7 @@ export const pushPreparedDirectoryToPreview = async ({
|
|
|
40
40
|
);
|
|
41
41
|
|
|
42
42
|
return retryAsync(
|
|
43
|
-
() => uploadPreviewZip(apiBaseUrl, apiKey, storeId, previewKey, zipBuffer,
|
|
43
|
+
() => uploadPreviewZip(apiBaseUrl, apiKey, storeId, previewKey, zipBuffer, !overrideState),
|
|
44
44
|
{
|
|
45
45
|
attempts: 3,
|
|
46
46
|
shouldRetry: (uploadResult) => !uploadResult.ok && Boolean(uploadResult.retriable),
|
|
@@ -54,11 +54,11 @@ export const pushPreparedDirectoryToPreview = async ({
|
|
|
54
54
|
);
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
export const push = async ({ skipBuild = false, previewKey: previewKeyArg,
|
|
57
|
+
export const push = async ({ skipBuild = false, previewKey: previewKeyArg, overrideState = false } = {}) => {
|
|
58
58
|
const { config, credentials } = await loadConfigOrFail();
|
|
59
59
|
|
|
60
60
|
if (!skipBuild) {
|
|
61
|
-
const result = await build({
|
|
61
|
+
const result = await build({ overrideState });
|
|
62
62
|
if (!result.ok) {
|
|
63
63
|
process.exit(1);
|
|
64
64
|
}
|
|
@@ -89,7 +89,7 @@ export const push = async ({ skipBuild = false, previewKey: previewKeyArg, skipI
|
|
|
89
89
|
previewKey,
|
|
90
90
|
rootDir,
|
|
91
91
|
spinner,
|
|
92
|
-
|
|
92
|
+
overrideState,
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
if (!result.ok) {
|
|
@@ -99,5 +99,8 @@ export const push = async ({ skipBuild = false, previewKey: previewKeyArg, skipI
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
spinner.stop(`Files uploaded to preview ${previewKey}.`);
|
|
102
|
+
ui.log.message(
|
|
103
|
+
`Theme state: ${overrideState ? "overridden from local files" : "preserved from the theme editor"}`,
|
|
104
|
+
);
|
|
102
105
|
ui.log.message(` ${previewDetails.data.url}`);
|
|
103
106
|
};
|
package/lib/stores.mjs
CHANGED
|
@@ -66,7 +66,7 @@ export const storesSet = async (storeIdArg) => {
|
|
|
66
66
|
spinner.stop(`Active store set to ${selectedStore.name} (ID: ${selectedStore.id}).`);
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
-
export const formatInitSummary = ({ apiBaseUrl, usedDefaultBaseUrl, stores, selectedStore }) => {
|
|
69
|
+
export const formatInitSummary = ({ apiBaseUrl, usedDefaultBaseUrl, stores, selectedStore, previewKey }) => {
|
|
70
70
|
const lines = ["Status: Connected."];
|
|
71
71
|
|
|
72
72
|
if (usedDefaultBaseUrl) {
|
|
@@ -76,7 +76,12 @@ export const formatInitSummary = ({ apiBaseUrl, usedDefaultBaseUrl, stores, sele
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
if (selectedStore) {
|
|
79
|
-
lines.push(`Store: ${selectedStore.name} (ID: ${selectedStore.id}) [auto-selected]`);
|
|
79
|
+
lines.push(`Store: ${selectedStore.name} (ID: ${selectedStore.id})${stores.length === 1 ? " [auto-selected]" : ""}`);
|
|
80
|
+
if (previewKey) {
|
|
81
|
+
lines.push(`Preview: ${previewKey} [attached]`);
|
|
82
|
+
} else {
|
|
83
|
+
lines.push("Preview: live theme (no preview attached)");
|
|
84
|
+
}
|
|
80
85
|
return lines.join("\n");
|
|
81
86
|
}
|
|
82
87
|
|