gulp-mu-gulp-api 0.3.0 → 0.3.2
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 +18 -8
- package/package.json +1 -1
- package/src/i18x.mjs +97 -20
- package/src/index.mjs +97 -6
package/README.md
CHANGED
|
@@ -20,6 +20,11 @@ The module is deliberately **dependency-free** and knows nothing about µGulp™
|
|
|
20
20
|
npm install gulp-mu-gulp-api
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
### 0.3.2
|
|
24
|
+
|
|
25
|
+
- **Added** `FormatValue()` — renders `<name format="…"/>` placeholders from `<format …>` XML definitions in the consumer project's `i18x/gulp/<lid>.json`.
|
|
26
|
+
- **Changed** built-in format IDs to CamelCase (`floatFix2`, `stdDate`, `progressDuration`, `byteSize`, …); lifecycle duration logs expect raw milliseconds with `format="progressDuration"`.
|
|
27
|
+
|
|
23
28
|
### 0.3.0
|
|
24
29
|
|
|
25
30
|
- **Added** integrated i18x console layer: `Log`, `Warn`, `LogError`, `Translate`, `GetLid`, `SetLid`, `GetTimeZone`, `DateInTimeZone`, `InstallStringExtensions` — translations live in the consumer project's `i18x/gulp/<lid>.json`; language follows `MICROGULP_LANG` (set by the µGulp dashboard) with locale fallback.
|
|
@@ -83,7 +88,7 @@ let values = await RequestForm({
|
|
|
83
88
|
|
|
84
89
|
| Function | Purpose |
|
|
85
90
|
| :--- | :--- |
|
|
86
|
-
| `IsMicroGulp()` / `IsµGulp()` | `true` when the task runs under µGulp™ with an attached dashboard
|
|
91
|
+
| `IsMicroGulp()` / `IsµGulp()` | `true` when the task runs under µGulp™ with an attached dashboard |
|
|
87
92
|
| `InstallStringExtensions()` | Installs µLib-compatible `String.prototype.i18xTrans()`/`i18xRegister()` (auto-run on import) |
|
|
88
93
|
| `Log(text, values?)` / `Warn` / `LogError` | Localized `console.*` via i18x (`<context="task log"/>` etc.) |
|
|
89
94
|
| `Translate(text, values?)` | Returns the translated string without logging |
|
|
@@ -105,8 +110,8 @@ Write the i18xe context tag **into** the source phrase and mark it with `.i18xRe
|
|
|
105
110
|
|
|
106
111
|
| Placeholder | Source |
|
|
107
112
|
| :--- | :--- |
|
|
108
|
-
| `version` | `µ
|
|
109
|
-
| `project` | `µ
|
|
113
|
+
| `version` | `µI18xContext.version` export, else `package.json` |
|
|
114
|
+
| `project` | `µI18xContext.project` export, else `package.json` `name` |
|
|
110
115
|
| `workspace` | workspace folder name |
|
|
111
116
|
| `source` | gulpfile path relative to the workspace |
|
|
112
117
|
| `gulpfile` | gulpfile file name |
|
|
@@ -116,7 +121,7 @@ Write the i18xe context tag **into** the source phrase and mark it with `.i18xRe
|
|
|
116
121
|
Export defaults from the gulpfile (overrides `package.json`):
|
|
117
122
|
|
|
118
123
|
```javascript
|
|
119
|
-
export const µ
|
|
124
|
+
export const µI18xContext = { version: '1.13', project: 'eMP' };
|
|
120
125
|
|
|
121
126
|
MAKE_BUILDS.µDisplayName = 'Make Build V<version/><context="µDisplayName"/>'.i18xRegister();
|
|
122
127
|
// => "Make Build V1.13" in the dashboard (translatable per language)
|
|
@@ -178,6 +183,11 @@ Das Modul ist bewusst **abhängigkeitsfrei** und kennt keine µGulp™-Interna:
|
|
|
178
183
|
npm install gulp-mu-gulp-api
|
|
179
184
|
```
|
|
180
185
|
|
|
186
|
+
### 0.3.2
|
|
187
|
+
|
|
188
|
+
- **Neu** `FormatValue()` — rendert `<name format="…"/>`-Platzhalter aus `<format …>`-XML-Definitionen in `i18x/gulp/<lid>.json` des Verbraucherprojekts.
|
|
189
|
+
- **Geändert** Format-IDs auf CamelCase (`floatFix2`, `stdDate`, `progressDuration`, `byteSize`, …); Lifecycle-Dauer-Logs erwarten Roh-Millisekunden mit `format="progressDuration"`.
|
|
190
|
+
|
|
181
191
|
### 0.3.0
|
|
182
192
|
|
|
183
193
|
- **Neu** integrierte i18x-Konsolenschicht: `Log`, `Warn`, `LogError`, `Translate`, `GetLid`, `SetLid`, `GetTimeZone`, `DateInTimeZone`, `InstallStringExtensions` — Übersetzungen liegen im Verbraucherprojekt unter `i18x/gulp/<lid>.json`; die Sprache folgt `MICROGULP_LANG` (vom µGulp-Dashboard gesetzt) mit Locale-Fallback.
|
|
@@ -241,7 +251,7 @@ let values = await RequestForm({
|
|
|
241
251
|
|
|
242
252
|
| Funktion | Zweck |
|
|
243
253
|
| :--- | :--- |
|
|
244
|
-
| `IsMicroGulp()` / `IsµGulp()` | `true`, wenn der Task unter µGulp™ mit angebundenem Dashboard läuft
|
|
254
|
+
| `IsMicroGulp()` / `IsµGulp()` | `true`, wenn der Task unter µGulp™ mit angebundenem Dashboard läuft |
|
|
245
255
|
| `InstallStringExtensions()` | Installiert µLib-kompatible `String.prototype.i18xTrans()`/`i18xRegister()` (läuft beim Import automatisch) |
|
|
246
256
|
| `Log(text, values?)` / `Warn` / `LogError` | Lokalisierte `console.*`-Ausgabe über i18x (`<context="task log"/>` usw.) |
|
|
247
257
|
| `Translate(text, values?)` | Gibt den übersetzten String zurück, ohne zu loggen |
|
|
@@ -263,8 +273,8 @@ Den i18xe-Kontext-Tag **in** die Textphrase schreiben und mit `.i18xRegister()`
|
|
|
263
273
|
|
|
264
274
|
| Platzhalter | Quelle |
|
|
265
275
|
| :--- | :--- |
|
|
266
|
-
| `version` | `µ
|
|
267
|
-
| `project` | `µ
|
|
276
|
+
| `version` | `µI18xContext.version`-Export, sonst `package.json` |
|
|
277
|
+
| `project` | `µI18xContext.project`-Export, sonst `package.json` `name` |
|
|
268
278
|
| `workspace` | Workspace-Ordnername |
|
|
269
279
|
| `source` | Gulpfile-Pfad relativ zum Workspace |
|
|
270
280
|
| `gulpfile` | Gulpfile-Dateiname |
|
|
@@ -274,7 +284,7 @@ Den i18xe-Kontext-Tag **in** die Textphrase schreiben und mit `.i18xRegister()`
|
|
|
274
284
|
Defaults im Gulpfile exportieren (`package.json` wird überschrieben):
|
|
275
285
|
|
|
276
286
|
```javascript
|
|
277
|
-
export const µ
|
|
287
|
+
export const µI18xContext = { version: '1.13', project: 'eMP' };
|
|
278
288
|
|
|
279
289
|
MAKE_BUILDS.µDisplayName = 'Make Build V<version/><context="µDisplayName"/>'.i18xRegister();
|
|
280
290
|
// => „Make Build V1.13“ im Dashboard (pro Sprache übersetzbar)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gulp-mu-gulp-api",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Public task API for the µGulp orchestrator: progress reporting, sound signals, speech output, localized console output (i18x) and interactive UI inputs (text, password, number, textarea, color, font, select, radio, multi-select checkbox, range slider, date/time, file) from within gulp tasks — with graceful CLI fallbacks when running without µGulp.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.mjs",
|
package/src/i18x.mjs
CHANGED
|
@@ -50,6 +50,7 @@ const LIDS_MAIN_CULTURES = {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
let dictionaryCache = new Map();
|
|
53
|
+
let formatDefinitionCache = new Map();
|
|
53
54
|
let availableLids = null;
|
|
54
55
|
let activeLid = null;
|
|
55
56
|
const timeZoneFormatters = {};
|
|
@@ -175,6 +176,7 @@ export function GetLid() {
|
|
|
175
176
|
*/
|
|
176
177
|
export function SetLid(_lid) {
|
|
177
178
|
activeLid = _lid ? _BestAvailableLid(_lid, _AvailableLids()) : null;
|
|
179
|
+
formatDefinitionCache.clear();
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
/** Loads and caches `<dir>/<lid>.json`; a missing/broken file yields source text. */
|
|
@@ -191,29 +193,104 @@ function _LoadDictionary(_lid) {
|
|
|
191
193
|
return dictionary;
|
|
192
194
|
}
|
|
193
195
|
|
|
194
|
-
/**
|
|
196
|
+
/** @param {Record<string, string>} _dictionary */
|
|
197
|
+
function _ExtractFormatDefinitions(_dictionary) {
|
|
198
|
+
let formats = {};
|
|
199
|
+
for (let [key, value] of Object.entries(_dictionary)) {
|
|
200
|
+
if (!key.startsWith('<format name=')) continue;
|
|
201
|
+
let match = key.match(/<format name="([^"]+)"/);
|
|
202
|
+
if (match) formats[match[1]] = value;
|
|
203
|
+
}
|
|
204
|
+
return formats;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Localized i18xe format XML keyed by format id (from i18x/gulp dictionaries). */
|
|
208
|
+
function _LoadFormatDefinitions(_lid) {
|
|
209
|
+
if (formatDefinitionCache.has(_lid)) return formatDefinitionCache.get(_lid);
|
|
210
|
+
let formats = { ..._ExtractFormatDefinitions(_LoadDictionary(DEFAULT_LID)) };
|
|
211
|
+
if (_lid !== DEFAULT_LID) {
|
|
212
|
+
Object.assign(formats, _ExtractFormatDefinitions(_LoadDictionary(_lid)));
|
|
213
|
+
}
|
|
214
|
+
formatDefinitionCache.set(_lid, formats);
|
|
215
|
+
return formats;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Formats milliseconds as M:SS or H:MM:SS (progress bar durations). */
|
|
219
|
+
function _FormatDuration(_ms) {
|
|
220
|
+
let ms = Math.max(0, Number(_ms) || 0);
|
|
221
|
+
let totalSeconds = Math.round(ms / 1000);
|
|
222
|
+
let seconds = totalSeconds % 60;
|
|
223
|
+
let minutes = Math.floor(totalSeconds / 60) % 60;
|
|
224
|
+
let hours = Math.floor(totalSeconds / 3600);
|
|
225
|
+
let pad = (_n) => String(_n).padStart(2, '0');
|
|
226
|
+
return hours > 0 ? hours + ':' + pad(minutes) + ':' + pad(seconds) : minutes + ':' + pad(seconds);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Formats a numeric value with a named i18xe format definition from the
|
|
231
|
+
* project's i18x/gulp dictionaries (synced from i18xe prod).
|
|
232
|
+
* @param {number|string} _value
|
|
233
|
+
* @param {string} _formatName e.g. 'int', 'floatFix2', 'byteSize'
|
|
234
|
+
* @param {string} [_lid]
|
|
235
|
+
* @returns {string}
|
|
236
|
+
*/
|
|
237
|
+
export function FormatValue(_value, _formatName, _lid) {
|
|
238
|
+
let lid = _lid ?? GetLid();
|
|
239
|
+
let tz = GetTimeZone();
|
|
240
|
+
if (_formatName === 'progressDuration') return _FormatDuration(Number(_value));
|
|
241
|
+
if (_formatName === 'stdTime') {
|
|
242
|
+
let date = new Date(Number(_value) || 0);
|
|
243
|
+
try {
|
|
244
|
+
return new Intl.DateTimeFormat(lid, { hour: '2-digit', minute: '2-digit', timeZone: tz }).format(date);
|
|
245
|
+
} catch {
|
|
246
|
+
return date.toISOString();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (_formatName === 'stdDateTime' || _formatName === 'fullDateTime') {
|
|
250
|
+
let date = new Date(Number(_value) || 0);
|
|
251
|
+
try {
|
|
252
|
+
return new Intl.DateTimeFormat(lid, {
|
|
253
|
+
year: 'numeric', month: '2-digit', day: '2-digit',
|
|
254
|
+
hour: '2-digit', minute: '2-digit', timeZone: tz,
|
|
255
|
+
}).format(date);
|
|
256
|
+
} catch {
|
|
257
|
+
return date.toISOString();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (_formatName === 'byteSize') {
|
|
261
|
+
let bytes = Number(_value);
|
|
262
|
+
if (!Number.isFinite(bytes)) return String(_value);
|
|
263
|
+
if (bytes >= 1073741824) return FormatValue(bytes / 1073741824, 'floatFix2', lid) + ' GB';
|
|
264
|
+
if (bytes >= 1048576) return FormatValue(bytes / 1048576, 'floatFix2', lid) + ' MB';
|
|
265
|
+
if (bytes >= 1024) return FormatValue(bytes / 1024, 'floatFix2', lid) + ' KB';
|
|
266
|
+
return FormatValue(bytes, 'int', lid) + ' Bytes';
|
|
267
|
+
}
|
|
268
|
+
let number = Number(_value);
|
|
269
|
+
if (!Number.isFinite(number)) return String(_value);
|
|
270
|
+
let definition = _LoadFormatDefinitions(lid)[_formatName];
|
|
271
|
+
if (!definition) return String(_value);
|
|
272
|
+
let groupSeparator = definition.match(/<xa if="1000\|1000\+">([^<]*)<\/xa>/)?.[1] ?? ',';
|
|
273
|
+
let decimalSeparator = definition.match(/if="0~">([^<]*)<m0\/>/)?.[1]
|
|
274
|
+
?? definition.match(/<\/xa>([^<]*)<f0\/>/)?.[1] ?? '.';
|
|
275
|
+
let fractionDigits = _formatName === 'floatFix1' ? 1
|
|
276
|
+
: (_formatName.startsWith('floatFix') ? 2 : 0);
|
|
277
|
+
if (_formatName === 'int') number = Math.round(number);
|
|
278
|
+
let negative = number < 0;
|
|
279
|
+
let absolute = Math.abs(number);
|
|
280
|
+
let integerPart = String(Math.trunc(fractionDigits ? absolute : Math.round(absolute)));
|
|
281
|
+
let grouped = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, groupSeparator);
|
|
282
|
+
let fraction = fractionDigits ? decimalSeparator + absolute.toFixed(fractionDigits).split('.')[1] : '';
|
|
283
|
+
return (negative ? '-' : '') + grouped + fraction;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Substitutes `<name/>` placeholders; honors `format="…"` via FormatValue. */
|
|
195
287
|
function _ApplyPlaceholders(_text, _values) {
|
|
196
288
|
let text = String(_text);
|
|
289
|
+
let lid = GetLid();
|
|
197
290
|
for (let key of Object.keys(_values ?? {})) {
|
|
198
291
|
let pattern = new RegExp('<' + key + '(?:\\s+format="([^"]*)")?\\s*/>', 'g');
|
|
199
|
-
text = text.replace(pattern, (_match, _formatName) =>
|
|
200
|
-
|
|
201
|
-
if (_formatName === 'stdtime' || _formatName === 'stddatetime') {
|
|
202
|
-
let date = new Date(Number(_values[key]) || 0);
|
|
203
|
-
let lid = GetLid();
|
|
204
|
-
try {
|
|
205
|
-
return new Intl.DateTimeFormat(lid, {
|
|
206
|
-
...( _formatName === 'stddatetime'
|
|
207
|
-
? { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }
|
|
208
|
-
: { hour: '2-digit', minute: '2-digit' }),
|
|
209
|
-
timeZone: GetTimeZone(),
|
|
210
|
-
}).format(date);
|
|
211
|
-
} catch {
|
|
212
|
-
return date.toISOString();
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return String(_values[key]);
|
|
216
|
-
});
|
|
292
|
+
text = text.replace(pattern, (_match, _formatName) =>
|
|
293
|
+
_formatName ? FormatValue(_values[key], _formatName, lid) : String(_values[key]));
|
|
217
294
|
}
|
|
218
295
|
return text.replace(/<[^>]*>/g, '');
|
|
219
296
|
}
|
|
@@ -302,5 +379,5 @@ InstallStringExtensions();
|
|
|
302
379
|
|
|
303
380
|
export default {
|
|
304
381
|
GetLid, SetLid, GetTimeZone, DateInTimeZone,
|
|
305
|
-
Translate, Log, Warn, LogError, InstallStringExtensions,
|
|
382
|
+
Translate, FormatValue, Log, Warn, LogError, InstallStringExtensions,
|
|
306
383
|
};
|
package/src/index.mjs
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
// Localized console output (i18x). Re-exported so tasks import everything
|
|
32
32
|
// from one place: `import { Log, Warn, ReportProgress } from 'gulp-mu-gulp-api'`.
|
|
33
|
-
export { Log, Warn, LogError, Translate, GetLid, SetLid, GetTimeZone, DateInTimeZone, InstallStringExtensions } from './i18x.mjs';
|
|
33
|
+
export { Log, Warn, LogError, Translate, FormatValue, GetLid, SetLid, GetTimeZone, DateInTimeZone, InstallStringExtensions } from './i18x.mjs';
|
|
34
34
|
import * as I18x from './i18x.mjs';
|
|
35
35
|
|
|
36
36
|
let uiRequestCounter = 0;
|
|
@@ -43,15 +43,12 @@ const pendingUiRequests = new Map();
|
|
|
43
43
|
* attached dashboard (progress and inputs are rendered in the webview)
|
|
44
44
|
*/
|
|
45
45
|
export function IsMicroGulp() {
|
|
46
|
-
return typeof process.send === 'function' && process.env.
|
|
46
|
+
return typeof process.send === 'function' && process.env.MICROGULP === '1';
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/** Brand-aligned alias for {@link IsMicroGulp}. */
|
|
50
50
|
export const IsµGulp = IsMicroGulp;
|
|
51
51
|
|
|
52
|
-
/** @deprecated Use {@link IsMicroGulp} or {@link IsµGulp}. */
|
|
53
|
-
export const IsUGulp = IsMicroGulp;
|
|
54
|
-
|
|
55
52
|
/*
|
|
56
53
|
* Task metadata (µDisplayName, µDescription, µTooltip, µGroup) is localized
|
|
57
54
|
* the same way as any other i18x phrase: with the context tag written *into*
|
|
@@ -147,6 +144,72 @@ export function LogTree(_spec) {
|
|
|
147
144
|
for (let line of _AsciiTreeLines(payload)) console.log(line);
|
|
148
145
|
}
|
|
149
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Sends an image gallery to the dashboard log pane. Each item needs a `src`
|
|
149
|
+
* (data URI or https URL) and a `label`; optional `caption` and per-item `title`.
|
|
150
|
+
* CLI fallback: lists labels and src lengths on stdout.
|
|
151
|
+
*
|
|
152
|
+
* @param {object} _spec `{ title?, columns?, items: { label, src, caption?, title? }[] }`
|
|
153
|
+
*/
|
|
154
|
+
export function LogGallery(_spec) {
|
|
155
|
+
let payload = _NormalizeGallerySpec(_spec);
|
|
156
|
+
if (IsMicroGulp()) {
|
|
157
|
+
process.send({ type: 'structured-log', format: 'gallery', payload });
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
console.log(_AsciiGallery(payload));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Renders µCSS BuildSkin `report.debug.previews` as a thumbnail gallery.
|
|
165
|
+
* Items without `thumbDataUri` are omitted (paths-only assets stay in the text log).
|
|
166
|
+
*
|
|
167
|
+
* @param {object[]} _previews preview entries from `report.debug.previews`
|
|
168
|
+
* @param {object} [_options] `{ title?, columns? }`
|
|
169
|
+
*/
|
|
170
|
+
export function LogAssetPreviews(_previews, _options = {}) {
|
|
171
|
+
let items = (_previews ?? [])
|
|
172
|
+
.filter((_entry) => _entry?.thumbDataUri)
|
|
173
|
+
.map((_entry) => ({
|
|
174
|
+
label: String(_entry.relPath ?? _entry.path ?? ''),
|
|
175
|
+
src: _entry.thumbDataUri,
|
|
176
|
+
caption: [
|
|
177
|
+
_entry.step,
|
|
178
|
+
_entry.skipped ? 'cached' : null,
|
|
179
|
+
_entry.width && _entry.height ? `${_entry.width}\u00d7${_entry.height}` : null,
|
|
180
|
+
].filter(Boolean).join(' \u00b7 '),
|
|
181
|
+
}));
|
|
182
|
+
if (!items.length) return;
|
|
183
|
+
LogGallery({
|
|
184
|
+
title: _options.title,
|
|
185
|
+
columns: _options.columns ?? 4,
|
|
186
|
+
items,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Convenience wrapper for a full µCSS `BuildSkin` debug block: summary table
|
|
192
|
+
* plus thumbnail gallery when `report.debug` is present.
|
|
193
|
+
*
|
|
194
|
+
* @param {object} _report BuildSkin return value
|
|
195
|
+
* @param {object} [_options] `{ title?, columns?, table?: boolean }`
|
|
196
|
+
*/
|
|
197
|
+
export function LogBuildDebugReport(_report, _options = {}) {
|
|
198
|
+
let debug = _report?.debug;
|
|
199
|
+
if (!debug) return;
|
|
200
|
+
if (_options.table !== false && debug.summary) {
|
|
201
|
+
LogTable({
|
|
202
|
+
columns: [{ id: 'metric', label: 'Metric' }, { id: 'value', label: 'Value' }],
|
|
203
|
+
rows: Object.entries(debug.summary).map(([metric, value]) => ({ metric, value: String(value) })),
|
|
204
|
+
hyphenate: false,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
LogAssetPreviews(debug.previews, {
|
|
208
|
+
title: _options.title ?? 'Generated assets',
|
|
209
|
+
columns: _options.columns,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
150
213
|
// -------------------------------------------------
|
|
151
214
|
// sound & speech
|
|
152
215
|
// -------------------------------------------------
|
|
@@ -425,10 +488,35 @@ function _AsciiTreeLines(_payload, _indent = '', _roots = null) {
|
|
|
425
488
|
return lines;
|
|
426
489
|
}
|
|
427
490
|
|
|
491
|
+
function _NormalizeGallerySpec(_spec) {
|
|
492
|
+
let columns = Math.max(1, Math.min(8, Number(_spec?.columns) || 4));
|
|
493
|
+
let items = (_spec?.items ?? []).map((_item) => ({
|
|
494
|
+
label: String(_item?.label ?? ''),
|
|
495
|
+
src: String(_item?.src ?? ''),
|
|
496
|
+
caption: _item?.caption != null ? String(_item.caption) : '',
|
|
497
|
+
title: _item?.title != null ? String(_item.title) : '',
|
|
498
|
+
})).filter((_item) => _item.src);
|
|
499
|
+
return {
|
|
500
|
+
title: _spec?.title != null ? String(_spec.title) : '',
|
|
501
|
+
columns,
|
|
502
|
+
items,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function _AsciiGallery(_payload) {
|
|
507
|
+
let lines = [];
|
|
508
|
+
if (_payload.title) lines.push(_payload.title);
|
|
509
|
+
for (let item of _payload.items) {
|
|
510
|
+
let meta = item.caption ? ` (${item.caption})` : '';
|
|
511
|
+
let srcHint = item.src.startsWith('data:') ? `[data URI, ${item.src.length} chars]` : item.src;
|
|
512
|
+
lines.push(` ${item.label}${meta}: ${srcHint}`);
|
|
513
|
+
}
|
|
514
|
+
return lines.join('\n');
|
|
515
|
+
}
|
|
516
|
+
|
|
428
517
|
export default {
|
|
429
518
|
IsMicroGulp,
|
|
430
519
|
IsµGulp,
|
|
431
|
-
IsUGulp,
|
|
432
520
|
ReportProgress,
|
|
433
521
|
CreateProgress,
|
|
434
522
|
PlaySignal,
|
|
@@ -450,4 +538,7 @@ export default {
|
|
|
450
538
|
InstallStringExtensions: I18x.InstallStringExtensions,
|
|
451
539
|
LogTable,
|
|
452
540
|
LogTree,
|
|
541
|
+
LogGallery,
|
|
542
|
+
LogAssetPreviews,
|
|
543
|
+
LogBuildDebugReport,
|
|
453
544
|
};
|