@syntax-syllogism/flow-delta 0.6.1 → 0.6.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/dist/flexipage-cli.js +205 -50
- package/package.json +1 -1
package/dist/flexipage-cli.js
CHANGED
|
@@ -126,11 +126,11 @@ function diffPage(oldModel, newModel) {
|
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
128
|
function diffRegion(name, oldRegion, newRegion) {
|
|
129
|
-
if (!oldRegion) return { name, type: newRegion.type, mode: newRegion.mode, status: "added", items: newRegion.items.map((item, index) => classifyItem(void 0, item, `${name}:after:${index}
|
|
130
|
-
if (!newRegion) return { name, type: oldRegion.type, mode: oldRegion.mode, status: "deleted", items: oldRegion.items.map((item, index) => classifyItem(item, void 0, `${name}:before:${index}
|
|
129
|
+
if (!oldRegion) return { name, path: name, type: newRegion.type, mode: newRegion.mode, status: "added", items: newRegion.items.map((item, index) => classifyItem(void 0, item, `${name}:after:${index}`, name)) };
|
|
130
|
+
if (!newRegion) return { name, path: name, type: oldRegion.type, mode: oldRegion.mode, status: "deleted", items: oldRegion.items.map((item, index) => classifyItem(item, void 0, `${name}:before:${index}`, name)) };
|
|
131
131
|
const items = diffItems(oldRegion.items, newRegion.items, name);
|
|
132
132
|
const regionChanges = deepDiff({ type: oldRegion.type, mode: oldRegion.mode }, { type: newRegion.type, mode: newRegion.mode });
|
|
133
|
-
return { name, type: newRegion.type, mode: newRegion.mode, status: regionChanges.length || items.some((item) => item.status !== "unchanged") ? "modified" : "unchanged", ...regionChanges.length ? { changes: regionChanges } : {}, items };
|
|
133
|
+
return { name, path: name, type: newRegion.type, mode: newRegion.mode, status: regionChanges.length || items.some((item) => item.status !== "unchanged") ? "modified" : "unchanged", ...regionChanges.length ? { changes: regionChanges } : {}, items };
|
|
134
134
|
}
|
|
135
135
|
function diffItems(oldItems, newItems, regionName) {
|
|
136
136
|
const pairs = lcs(oldItems, newItems);
|
|
@@ -138,22 +138,24 @@ function diffItems(oldItems, newItems, regionName) {
|
|
|
138
138
|
let oldIndex = 0;
|
|
139
139
|
let newIndex = 0;
|
|
140
140
|
for (const [matchOld, matchNew] of pairs) {
|
|
141
|
-
while (oldIndex < matchOld) output.push(classifyItem(oldItems[oldIndex], void 0, `${regionName}:before:${oldIndex++}
|
|
142
|
-
while (newIndex < matchNew) output.push(classifyItem(void 0, newItems[newIndex], `${regionName}:after:${newIndex++}
|
|
143
|
-
output.push(classifyItem(oldItems[matchOld], newItems[matchNew], `${regionName}:${matchNew}
|
|
141
|
+
while (oldIndex < matchOld) output.push(classifyItem(oldItems[oldIndex], void 0, `${regionName}:before:${oldIndex++}`, regionName));
|
|
142
|
+
while (newIndex < matchNew) output.push(classifyItem(void 0, newItems[newIndex], `${regionName}:after:${newIndex++}`, regionName));
|
|
143
|
+
output.push(classifyItem(oldItems[matchOld], newItems[matchNew], `${regionName}:${matchNew}`, regionName));
|
|
144
144
|
oldIndex = matchOld + 1;
|
|
145
145
|
newIndex = matchNew + 1;
|
|
146
146
|
}
|
|
147
|
-
while (oldIndex < oldItems.length) output.push(classifyItem(oldItems[oldIndex], void 0, `${regionName}:before:${oldIndex++}
|
|
148
|
-
while (newIndex < newItems.length) output.push(classifyItem(void 0, newItems[newIndex], `${regionName}:after:${newIndex++}
|
|
147
|
+
while (oldIndex < oldItems.length) output.push(classifyItem(oldItems[oldIndex], void 0, `${regionName}:before:${oldIndex++}`, regionName));
|
|
148
|
+
while (newIndex < newItems.length) output.push(classifyItem(void 0, newItems[newIndex], `${regionName}:after:${newIndex++}`, regionName));
|
|
149
149
|
return output;
|
|
150
150
|
}
|
|
151
|
-
function classifyItem(before, after, id) {
|
|
151
|
+
function classifyItem(before, after, id, regionPath) {
|
|
152
152
|
const item = after ?? before;
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
const identifier = item.identifier;
|
|
154
|
+
const itemPath = `${regionPath} \u203A ${itemLabel(item)}`;
|
|
155
|
+
const base = item.kind === "component" ? { id, path: itemPath, kind: item.kind, componentName: item.componentName, ...identifier ? { identifier } : {} } : { id, path: itemPath, kind: item.kind, fieldItem: item.fieldItem, ...identifier ? { identifier } : {} };
|
|
156
|
+
if (!before) return { ...base, status: "added", ...visibilityNote(item), after };
|
|
157
|
+
if (!after) return { ...base, status: "deleted", ...visibilityNote(item), before };
|
|
158
|
+
const changes = before.kind === "component" && after.kind === "component" ? deepDiff(before.properties, after.properties) : before.kind === "field" && after.kind === "field" ? deepDiff(before.attributes, after.attributes) : deepDiff(before, after);
|
|
157
159
|
return changes.length ? { ...base, status: "modified", changes, before, after } : { ...base, status: "unchanged", after };
|
|
158
160
|
}
|
|
159
161
|
function lcs(oldItems, newItems) {
|
|
@@ -178,7 +180,13 @@ function lcs(oldItems, newItems) {
|
|
|
178
180
|
return matches;
|
|
179
181
|
}
|
|
180
182
|
function itemKey(item) {
|
|
181
|
-
return item.kind === "component" ? `component:${item.componentName}` : `field:${item.fieldItem}`;
|
|
183
|
+
return item.kind === "component" ? `component:${item.componentName}#${item.identifier ?? ""}` : `field:${item.fieldItem}#${item.identifier ?? ""}`;
|
|
184
|
+
}
|
|
185
|
+
function itemLabel(item) {
|
|
186
|
+
return item.kind === "component" ? `${item.componentName}${item.identifier ? `#${item.identifier}` : ""}` : `${item.fieldItem}${item.identifier ? `#${item.identifier}` : ""}`;
|
|
187
|
+
}
|
|
188
|
+
function visibilityNote(item) {
|
|
189
|
+
return item.kind === "field" && item.attributes.visibilityRule !== void 0 ? { notes: ["has visibility rule"] } : {};
|
|
182
190
|
}
|
|
183
191
|
function orderOf(path) {
|
|
184
192
|
const index = HEADER_ORDER.indexOf(path);
|
|
@@ -193,6 +201,7 @@ import { Parser } from "xml2js";
|
|
|
193
201
|
import { createHash } from "node:crypto";
|
|
194
202
|
var FACET_GUID = /^Facet-[0-9a-f-]{36}$/i;
|
|
195
203
|
var HEADER_KEYS = ["masterLabel", "type", "sobjectType", "parentFlexiPage", "description"];
|
|
204
|
+
var ALWAYS_LIST_KEYS = /* @__PURE__ */ new Set(["criteria"]);
|
|
196
205
|
async function parseFlexiPage(xml) {
|
|
197
206
|
const parsed = await new Parser({ explicitArray: true, trim: true, normalize: false }).parseStringPromise(xml);
|
|
198
207
|
const page = parsed.FlexiPage;
|
|
@@ -233,6 +242,7 @@ function parseItemInstance(value) {
|
|
|
233
242
|
return [{
|
|
234
243
|
kind: "component",
|
|
235
244
|
componentName: scalar(component.componentName) ?? "(unnamed component)",
|
|
245
|
+
...scalar(component.identifier) !== void 0 ? { identifier: scalar(component.identifier) } : {},
|
|
236
246
|
properties: Object.fromEntries(properties),
|
|
237
247
|
facetRefs: []
|
|
238
248
|
}];
|
|
@@ -240,11 +250,13 @@ function parseItemInstance(value) {
|
|
|
240
250
|
const field = objectAt(instance.fieldInstance);
|
|
241
251
|
if (field) {
|
|
242
252
|
const attributes = Object.fromEntries(
|
|
243
|
-
Object.entries(field).filter(([key]) => key !== "fieldItem").map(([key, raw]) => [key, valueText(raw)]).filter((entry) => entry[1] !== void 0)
|
|
253
|
+
Object.entries(field).filter(([key]) => key !== "fieldItem" && key !== "identifier").map(([key, raw]) => [key, valueText(raw)]).filter((entry) => entry[1] !== void 0)
|
|
244
254
|
);
|
|
255
|
+
const identifier = scalar(field.identifier);
|
|
245
256
|
return [{
|
|
246
257
|
kind: "field",
|
|
247
258
|
fieldItem: scalar(field.fieldItem) ?? "(unnamed field)",
|
|
259
|
+
...identifier !== void 0 ? { identifier } : {},
|
|
248
260
|
attributes
|
|
249
261
|
}];
|
|
250
262
|
}
|
|
@@ -253,48 +265,88 @@ function parseItemInstance(value) {
|
|
|
253
265
|
function canonicalize(model) {
|
|
254
266
|
const rawRegions = model.regions;
|
|
255
267
|
const facets = new Map(rawRegions.filter((region) => region.type === "Facet").map((region) => [region.name, region]));
|
|
256
|
-
const references = /* @__PURE__ */ new Map();
|
|
257
|
-
for (const region of rawRegions) {
|
|
258
|
-
region.items.forEach((item) => {
|
|
259
|
-
if (item.kind !== "component") return;
|
|
260
|
-
for (const [property, value] of Object.entries(item.properties)) {
|
|
261
|
-
if (!facets.has(value)) continue;
|
|
262
|
-
const context = `${region.name}:${item.componentName}:${property}`;
|
|
263
|
-
references.set(value, [...references.get(value) ?? [], context]);
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
268
|
const canonicalNames = /* @__PURE__ */ new Map();
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
for (const facet of rawRegions.filter((
|
|
274
|
-
if (
|
|
275
|
-
|
|
276
|
-
|
|
269
|
+
const usedNames = new Set(rawRegions.filter((region) => region.type === "Region").map((region) => region.name));
|
|
270
|
+
for (const region of rawRegions.filter((candidate) => candidate.type === "Region")) {
|
|
271
|
+
resolveFacetChildren(region, region.name);
|
|
272
|
+
}
|
|
273
|
+
const orphanCounts = /* @__PURE__ */ new Map();
|
|
274
|
+
for (const facet of rawRegions.filter((candidate) => candidate.type === "Facet" && FACET_GUID.test(candidate.name))) {
|
|
275
|
+
if (canonicalNames.has(facet.name)) continue;
|
|
276
|
+
const base = `Facet:orphan:${facetHash(facet)}`;
|
|
277
|
+
const count = orphanCounts.get(base) ?? 0;
|
|
278
|
+
const name = count === 0 ? base : `${base}#${count + 1}`;
|
|
279
|
+
orphanCounts.set(base, count + 1);
|
|
280
|
+
canonicalNames.set(facet.name, name);
|
|
281
|
+
usedNames.add(name);
|
|
277
282
|
}
|
|
283
|
+
const canonicalFacetNames = new Set(canonicalNames.values());
|
|
278
284
|
const regions = rawRegions.map((region) => ({
|
|
279
285
|
...region,
|
|
280
286
|
name: canonicalNames.get(region.name) ?? region.name,
|
|
281
287
|
items: region.items.map((item) => {
|
|
282
288
|
if (item.kind !== "component") return item;
|
|
283
|
-
const properties = Object.fromEntries(Object.entries(item.properties).map(([key, value]) => [key,
|
|
289
|
+
const properties = Object.fromEntries(Object.entries(item.properties).map(([key, value]) => [key, mapFacetValues(value)]));
|
|
284
290
|
return {
|
|
285
291
|
...item,
|
|
286
292
|
properties,
|
|
287
|
-
facetRefs: Object.values(properties).
|
|
293
|
+
facetRefs: [...new Set(Object.values(properties).flatMap((value) => collectFacetRefs(value)))]
|
|
288
294
|
};
|
|
289
295
|
})
|
|
290
296
|
}));
|
|
297
|
+
const names = regions.map((region) => region.name);
|
|
298
|
+
if (names.length !== new Set(names).size) {
|
|
299
|
+
throw new Error("FlexiPage canonicalization produced duplicate region names");
|
|
300
|
+
}
|
|
291
301
|
return { ...model, regions };
|
|
302
|
+
function resolveFacetChildren(parent, parentPath) {
|
|
303
|
+
const siblingCounts = /* @__PURE__ */ new Map();
|
|
304
|
+
for (const item of parent.items) {
|
|
305
|
+
if (item.kind !== "component") continue;
|
|
306
|
+
for (const [property, value] of Object.entries(item.properties)) {
|
|
307
|
+
for (const facetName of collectRawFacetRefs(value)) {
|
|
308
|
+
const child = facets.get(facetName);
|
|
309
|
+
if (!child || !FACET_GUID.test(facetName)) continue;
|
|
310
|
+
let childPath = canonicalNames.get(facetName);
|
|
311
|
+
if (!childPath) {
|
|
312
|
+
const base = `${parentPath} \u203A ${componentLabel(item)} \u203A ${property}`;
|
|
313
|
+
const count = siblingCounts.get(base) ?? 0;
|
|
314
|
+
siblingCounts.set(base, count + 1);
|
|
315
|
+
childPath = count === 0 ? base : `${base}#${count + 1}`;
|
|
316
|
+
canonicalNames.set(facetName, childPath);
|
|
317
|
+
usedNames.add(childPath);
|
|
318
|
+
resolveFacetChildren(child, childPath);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function mapFacetValues(value) {
|
|
325
|
+
if (typeof value === "string") return canonicalNames.get(value) ?? value;
|
|
326
|
+
if (Array.isArray(value)) return value.map(mapFacetValues);
|
|
327
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, mapFacetValues(child)]));
|
|
328
|
+
}
|
|
329
|
+
function collectRawFacetRefs(value) {
|
|
330
|
+
if (typeof value === "string") return facets.has(value) ? [value] : [];
|
|
331
|
+
if (Array.isArray(value)) return value.flatMap(collectRawFacetRefs);
|
|
332
|
+
return Object.values(value).flatMap(collectRawFacetRefs);
|
|
333
|
+
}
|
|
334
|
+
function collectFacetRefs(value) {
|
|
335
|
+
if (typeof value === "string") {
|
|
336
|
+
const canonical = canonicalNames.get(value);
|
|
337
|
+
if (canonical) return [canonical];
|
|
338
|
+
if (facets.has(value)) return [value];
|
|
339
|
+
return canonicalFacetNames.has(value) ? [value] : [];
|
|
340
|
+
}
|
|
341
|
+
if (Array.isArray(value)) return value.flatMap(collectFacetRefs);
|
|
342
|
+
return Object.values(value).flatMap(collectFacetRefs);
|
|
343
|
+
}
|
|
292
344
|
}
|
|
293
345
|
function facetHash(region) {
|
|
294
346
|
const stable = {
|
|
295
347
|
type: region.type,
|
|
296
348
|
mode: region.mode,
|
|
297
|
-
items: region.items.map((item) => item.kind === "component" ? { kind: item.kind, componentName: item.componentName, properties: item.properties } : { kind: item.kind, fieldItem: item.fieldItem, attributes: item.attributes })
|
|
349
|
+
items: region.items.map((item) => item.kind === "component" ? { kind: item.kind, componentName: item.componentName, identifier: item.identifier, properties: item.properties } : { kind: item.kind, fieldItem: item.fieldItem, identifier: item.identifier, attributes: item.attributes })
|
|
298
350
|
};
|
|
299
351
|
return createHash("sha1").update(JSON.stringify(stable)).digest("hex").slice(0, 12);
|
|
300
352
|
}
|
|
@@ -313,13 +365,38 @@ function scalar(value) {
|
|
|
313
365
|
if (value === void 0 || value === null || typeof value === "object") return void 0;
|
|
314
366
|
return String(value);
|
|
315
367
|
}
|
|
316
|
-
function valueText(value) {
|
|
368
|
+
function valueText(value, preserveList = false) {
|
|
317
369
|
const direct = scalar(value);
|
|
318
370
|
if (direct !== void 0) return direct;
|
|
371
|
+
if (Array.isArray(value)) {
|
|
372
|
+
const values = value.map((entry) => valueText(entry)).filter((entry) => entry !== void 0);
|
|
373
|
+
if (values.length === 0) return void 0;
|
|
374
|
+
if (preserveList) return values;
|
|
375
|
+
return mergeNamedValues(values) ?? (values.length === 1 ? values[0] : values);
|
|
376
|
+
}
|
|
319
377
|
const object = objectAt(value);
|
|
320
378
|
if (!object) return void 0;
|
|
321
379
|
const entries = Object.entries(object).sort(([left], [right]) => left.localeCompare(right));
|
|
322
|
-
|
|
380
|
+
const name = scalar(object.name);
|
|
381
|
+
if (name !== void 0 && Object.prototype.hasOwnProperty.call(object, "value")) {
|
|
382
|
+
const nested = valueText(object.value);
|
|
383
|
+
return { [name]: nested ?? "" };
|
|
384
|
+
}
|
|
385
|
+
return Object.fromEntries(entries.map(([key, child]) => [key, valueText(child, ALWAYS_LIST_KEYS.has(key))]).filter((entry) => entry[1] !== void 0));
|
|
386
|
+
}
|
|
387
|
+
function mergeNamedValues(values) {
|
|
388
|
+
if (values.length === 0 || !values.every(isSingleEntryObject)) return void 0;
|
|
389
|
+
const entries = values.flatMap((value) => Object.entries(value));
|
|
390
|
+
if (new Set(entries.map(([key]) => key)).size !== entries.length) return void 0;
|
|
391
|
+
return Object.fromEntries(entries);
|
|
392
|
+
}
|
|
393
|
+
function isSingleEntryObject(value) {
|
|
394
|
+
return !Array.isArray(value) && typeof value === "object" && Object.keys(value).length === 1;
|
|
395
|
+
}
|
|
396
|
+
function componentLabel(item) {
|
|
397
|
+
const identity = item.identifier ? `${item.componentName}#${item.identifier}` : item.componentName;
|
|
398
|
+
const label = ["title", "label", "name"].map((key) => item.properties[key]).find((value) => typeof value === "string" && value.length > 0);
|
|
399
|
+
return label && label !== item.componentName ? `${identity} (${label})` : identity;
|
|
323
400
|
}
|
|
324
401
|
|
|
325
402
|
// src/render/shell.ts
|
|
@@ -355,6 +432,8 @@ function renderShell(options) {
|
|
|
355
432
|
:root[data-theme="dark"] { color-scheme:dark; --bg:#0f172a; --surface:#111827; --surface-muted:#1f2937; --surface-selected:#1e3a5f; --border:#334155; --focus:#60a5fa; --text:#e5e7eb; --muted:#a5b4c7; --faint:#718096; --added:#4ade80; --added-fill:#123f2a; --deleted:#f87171; --deleted-fill:#4a1d23; --modified:#fbbf24; --modified-fill:#46330d; --unchanged:#64748b; --unchanged-fill:#263241; }
|
|
356
433
|
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { color-scheme:dark; --bg:#0f172a; --surface:#111827; --surface-muted:#1f2937; --surface-selected:#1e3a5f; --border:#334155; --focus:#60a5fa; --text:#e5e7eb; --muted:#a5b4c7; --faint:#718096; --added:#4ade80; --added-fill:#123f2a; --deleted:#f87171; --deleted-fill:#4a1d23; --modified:#fbbf24; --modified-fill:#46330d; --unchanged:#64748b; --unchanged-fill:#263241; } }
|
|
357
434
|
* { box-sizing:border-box; } body { margin:0; height:100vh; display:grid; grid-template-rows:auto 1fr; background:var(--bg); color:var(--text); font-size:14px; line-height:1.45; } header { display:flex; justify-content:space-between; gap:16px; align-items:center; padding:12px 20px; border-bottom:1px solid var(--border); background:var(--surface); } h1 { margin:0 0 2px; font-size:15px; font-weight:650; } .meta { color:var(--muted); font-size:12.5px; } .header-actions { display:flex; flex-direction:column; align-items:flex-end; gap:9px; } .filters { display:flex; gap:7px; flex-wrap:wrap; justify-content:flex-end; } .display-controls { display:flex; align-items:center; justify-content:flex-end; gap:8px; } button { font:inherit; cursor:pointer; } .filters button, .theme-toggle button, .panel-toggle, .panel-reopen { border:1px solid var(--border); background:var(--surface-muted); color:var(--muted); border-radius:999px; padding:5px 10px; font-size:12px; font-weight:600; } .filters button.active, .theme-toggle button[aria-pressed="true"] { background:var(--surface-selected); border-color:var(--focus); color:var(--text); } .theme-toggle { display:inline-flex; gap:2px; padding:2px; border:1px solid var(--border); border-radius:999px; background:var(--surface-muted); } .theme-toggle button { border:0; padding:3px 8px; } main { position:relative; display:grid; grid-template-columns:minmax(0,1fr) clamp(300px,var(--panel-width),70vw); min-height:0; } body.panel-collapsed main { grid-template-columns:minmax(0,1fr) 0; } .canvas { overflow:auto; padding:24px; position:relative; } .outline { max-width:1100px; margin:0 auto; } .outline-region { margin:0 0 13px; border:1px solid var(--border); border-left-width:4px; border-radius:9px; background:var(--surface); overflow:hidden; } .outline-region.added { border-left-color:var(--added); } .outline-region.deleted { border-left-color:var(--deleted); } .outline-region.modified { border-left-color:var(--modified); } .outline-region.unchanged { border-left-color:var(--unchanged); } .region-head { display:flex; align-items:center; gap:8px; padding:10px 13px; background:var(--surface-muted); font-weight:700; } .region-items { padding:7px 10px 10px 25px; } .outline-row { display:flex; align-items:center; gap:8px; margin:4px 0; padding:8px 10px; border:1px solid var(--border); border-radius:7px; background:var(--surface); cursor:pointer; } .outline-row:hover, .outline-row:focus-visible { border-color:var(--focus); outline:none; } .outline-row.added { background:var(--added-fill); border-color:color-mix(in srgb,var(--added) 45%,var(--border)); } .outline-row.deleted { background:var(--deleted-fill); border-color:color-mix(in srgb,var(--deleted) 45%,var(--border)); } .outline-row.modified { background:var(--modified-fill); border-color:color-mix(in srgb,var(--modified) 45%,var(--border)); } .facet-region { margin:8px 0 3px 22px; } .facet-region .region-head { font-size:12px; padding:7px 10px; } .facet-region .region-items { padding-left:14px; } .status-badge { min-width:68px; color:var(--muted); font-size:10px; font-weight:750; text-transform:uppercase; letter-spacing:.04em; } .added .status-badge { color:var(--added); } .deleted .status-badge { color:var(--deleted); } .modified .status-badge { color:var(--modified); } .summary-stat { margin-right:10px; } .summary-stat.added { color:var(--added); } .summary-stat.deleted { color:var(--deleted); } .summary-stat.modified { color:var(--modified); } .banner { margin:0 auto 16px; max-width:1100px; padding:11px 14px; border:1px solid var(--modified); border-radius:8px; background:var(--modified-fill); color:var(--modified); font-weight:750; } .panel { position:relative; min-width:0; border-left:1px solid var(--border); background:var(--surface); padding:20px; overflow:auto; } body.panel-collapsed .panel { padding:0; border-left:0; overflow:hidden; } body.panel-collapsed .panel > *:not(.panel-resizer) { display:none; } .panel-resizer { position:absolute; left:0; top:0; bottom:0; width:9px; transform:translateX(-50%); cursor:col-resize; z-index:2; } .panel-resizer:hover { background:var(--focus); opacity:.5; } .panel-head { display:flex; justify-content:space-between; align-items:flex-start; gap:8px; } .panel h2 { margin:0 0 8px; font-size:15px; } .panel-toggle { border-radius:7px; padding:6px 9px; } .panel-reopen { display:none; position:absolute; top:14px; right:14px; z-index:3; border-radius:7px; } body.panel-collapsed .panel-reopen { display:block; } .panel-badge { display:inline-block; margin:0 0 15px; padding:3px 9px; border-radius:999px; background:var(--unchanged-fill); color:var(--muted); font-size:11px; font-weight:650; } .panel-badge.added { background:var(--added-fill); color:var(--added); } .panel-badge.deleted { background:var(--deleted-fill); color:var(--deleted); } .panel-badge.modified { background:var(--modified-fill); color:var(--modified); } .empty { color:var(--muted); font-size:13px; } .changes { list-style:none; margin:0; padding:0; } .changes li { margin:0 0 10px; } .change-path { display:block; margin-bottom:3px; color:var(--muted); font-size:11px; font-weight:700; } .val { padding:2px 5px; border-radius:4px; font:12px ui-monospace,Consolas,monospace; overflow-wrap:anywhere; } .val.before { color:var(--deleted); background:var(--deleted-fill); text-decoration:line-through; } .val.after { color:var(--added); background:var(--added-fill); } .arrow { color:var(--faint); margin:0 3px; } [hidden] { display:none !important; }
|
|
435
|
+
.item-note { margin-left:auto; padding:2px 6px; border:1px solid var(--border); border-radius:999px; background:var(--surface-muted); color:var(--muted); font-size:11px; white-space:nowrap; }
|
|
436
|
+
.item-notes { margin:0 0 10px; padding-left:18px; color:var(--muted); font-size:12px; }
|
|
358
437
|
.view-toggle { display:inline-flex; gap:2px; padding:2px; border:1px solid var(--border); border-radius:999px; background:var(--surface-muted); }
|
|
359
438
|
.view-toggle button { border:1px solid var(--border); background:var(--surface-muted); color:var(--muted); border-radius:999px; padding:3px 8px; font-size:12px; font-weight:600; }
|
|
360
439
|
.view-toggle button.active { background:var(--surface-selected); border-color:var(--focus); color:var(--text); }
|
|
@@ -374,6 +453,8 @@ function renderShell(options) {
|
|
|
374
453
|
.wireframe-cell.modified { border-color:var(--modified); }
|
|
375
454
|
.wireframe-cell.unchanged { border-color:var(--unchanged); }
|
|
376
455
|
.wireframe-slot-head { display:flex; align-items:center; gap:8px; padding:9px 11px; background:var(--surface-muted); font-weight:700; }
|
|
456
|
+
.wireframe-rollup { margin-left:auto; border:1px solid var(--modified); border-radius:999px; padding:3px 8px; background:var(--modified-fill); color:var(--modified); font-size:11px; font-weight:700; white-space:nowrap; }
|
|
457
|
+
.wireframe-rollup:hover, .wireframe-rollup:focus-visible { border-color:var(--text); outline:2px solid var(--focus); outline-offset:1px; }
|
|
377
458
|
.wireframe-items { min-height:56px; padding:7px 10px 10px; }
|
|
378
459
|
.wireframe-empty-label { display:block; padding:8px 2px; color:var(--faint); font-style:italic; }
|
|
379
460
|
.wireframe-stack-cell { padding:0; }
|
|
@@ -381,11 +462,26 @@ function renderShell(options) {
|
|
|
381
462
|
.wireframe-removed { margin-top:18px; padding:14px; border:1px dashed var(--deleted); border-radius:9px; background:var(--surface-muted); }
|
|
382
463
|
.wireframe-removed h2 { margin:0 0 10px; font-size:14px; color:var(--deleted); }
|
|
383
464
|
.wireframe-removed-region { margin-top:8px; border:1px solid var(--border); border-radius:7px; overflow:hidden; }
|
|
465
|
+
.panel-back { display:block; margin:0 0 7px; border:0; padding:0; background:transparent; color:var(--muted); font-size:12px; font-weight:650; text-align:left; }
|
|
466
|
+
.panel-back:hover, .panel-back:focus-visible { color:var(--text); text-decoration:underline; outline:none; }
|
|
467
|
+
.digest { display:grid; gap:14px; }
|
|
468
|
+
.digest-group { margin:0; }
|
|
469
|
+
.digest-group-head { display:flex; justify-content:space-between; gap:8px; margin:0 0 6px; color:var(--text); font-size:13px; font-weight:750; }
|
|
470
|
+
.digest-group-count { color:var(--muted); font-weight:650; }
|
|
471
|
+
.digest-entry { display:flex; align-items:center; gap:8px; width:100%; margin:4px 0; border:1px solid var(--border); border-radius:7px; padding:7px 9px; background:var(--surface); color:var(--text); text-align:left; }
|
|
472
|
+
.digest-entry.added { background:var(--added-fill); border-color:color-mix(in srgb,var(--added) 45%,var(--border)); }
|
|
473
|
+
.digest-entry.deleted { background:var(--deleted-fill); border-color:color-mix(in srgb,var(--deleted) 45%,var(--border)); }
|
|
474
|
+
.digest-entry.modified { background:var(--modified-fill); border-color:color-mix(in srgb,var(--modified) 45%,var(--border)); }
|
|
475
|
+
.digest-entry.added .status-badge { color:var(--added); }
|
|
476
|
+
.digest-entry.deleted .status-badge { color:var(--deleted); }
|
|
477
|
+
.digest-entry.modified .status-badge { color:var(--modified); }
|
|
478
|
+
.digest-entry:hover, .digest-entry:focus-visible { border-color:var(--focus); outline:none; }
|
|
479
|
+
.digest-entry .status-badge { min-width:58px; }
|
|
384
480
|
</style>
|
|
385
481
|
</head>
|
|
386
482
|
<body>
|
|
387
483
|
<header><div><h1>${productName} \u2014 ${title}</h1><div class="meta">${options.metaHtml}</div></div><div class="header-actions"><div class="filters" aria-label="View filters"><button class="filter-button active" data-view-mode="all">All</button><button class="filter-button" data-view-mode="after">After</button><button class="filter-button" data-view-mode="before">Before</button><button class="filter-button" data-view-mode="changes">Changes only</button></div><div class="display-controls">${options.wireframeHtml === void 0 ? "" : `<div class="view-toggle" aria-label="Canvas view"><button class="view-button" data-view-mode="outline">Outline</button><button class="view-button" data-view-mode="wireframe">Wireframe</button></div>`}<div class="theme-toggle" aria-label="Theme"><button class="theme-button" data-theme-choice="system">System</button><button class="theme-button" data-theme-choice="light">Light</button><button class="theme-button" data-theme-choice="dark">Dark</button></div></div></div></header>
|
|
388
|
-
<main><section class="canvas">${options.bannerHtml ?? ""}<div class="view-canvas" data-view-canvas="outline">${options.contentHtml}</div>${options.wireframeHtml === void 0 ? "" : `<div class="view-canvas" data-view-canvas="wireframe">${options.wireframeHtml}</div>`}</section><aside class="panel" id="detail-panel"><div class="panel-resizer" id="panel-resizer"></div><div class="panel-head"><h2 id="panel-title">Select a component or field</h2><button class="panel-toggle" id="panel-toggle" type="button" aria-expanded="true" aria-label="Collapse details" title="Collapse details">\u203A</button></div><div class="panel-badge" id="panel-badge" hidden></div><div id="panel-body" class="empty">Click a component or field row to inspect its property deltas.</div></aside><button class="panel-reopen" id="panel-reopen" type="button">Show details</button></main>
|
|
484
|
+
<main><section class="canvas">${options.bannerHtml ?? ""}<div class="view-canvas" data-view-canvas="outline">${options.contentHtml}</div>${options.wireframeHtml === void 0 ? "" : `<div class="view-canvas" data-view-canvas="wireframe">${options.wireframeHtml}</div>`}</section><aside class="panel" id="detail-panel"><div class="panel-resizer" id="panel-resizer"></div><div class="panel-head"><div><button class="panel-back" id="panel-back" type="button" hidden>Back to changes</button><h2 id="panel-title">Select a component or field</h2></div><button class="panel-toggle" id="panel-toggle" type="button" aria-expanded="true" aria-label="Collapse details" title="Collapse details">\u203A</button></div><div class="panel-badge" id="panel-badge" hidden></div><div id="panel-body" class="empty">Click a component or field row to inspect its property deltas.</div></aside><button class="panel-reopen" id="panel-reopen" type="button">Show details</button></main>
|
|
389
485
|
<script>(() => { const viewKey = ${JSON.stringify(VIEW_STORAGE_KEY)}; const viewButtons = [...document.querySelectorAll('.view-button')]; const hasWireframe = viewButtons.length > 0; function applyViewMode(mode) { const selected = hasWireframe && mode === "wireframe" ? "wireframe" : "outline"; document.documentElement.dataset.view = selected; viewButtons.forEach((button) => button.classList.toggle('active', button.dataset.viewMode === selected)); } viewButtons.forEach((button) => button.addEventListener('click', () => { const mode = button.dataset.viewMode; applyViewMode(mode); try { localStorage.setItem(viewKey, mode); } catch {} })); applyViewMode(document.documentElement.dataset.view ?? "outline"); })(); (() => { ${options.clientScript} })();</script>
|
|
390
486
|
</body>
|
|
391
487
|
</html>`;
|
|
@@ -397,7 +493,8 @@ function escapeHtml(value) {
|
|
|
397
493
|
// src/flexipage/render-helpers.ts
|
|
398
494
|
function renderItem(item) {
|
|
399
495
|
const label = item.componentName ?? item.fieldItem ?? "Item";
|
|
400
|
-
|
|
496
|
+
const notes = item.notes?.map((note) => `<span class="item-note">${escapeHtml2(note)}</span>`).join("") ?? "";
|
|
497
|
+
return `<div class="outline-row ${item.status}" data-status="${item.status}" data-item-id="${escapeHtml2(item.id)}" data-item-path="${escapeHtml2(item.path)}" tabindex="0" role="button"><span class="status-badge">${item.status}</span><span>${escapeHtml2(label)}</span>${notes}</div>`;
|
|
401
498
|
}
|
|
402
499
|
function itemFacetRefs(item) {
|
|
403
500
|
const value = item.after ?? item.before;
|
|
@@ -481,7 +578,38 @@ function deepFreeze(value) {
|
|
|
481
578
|
}
|
|
482
579
|
|
|
483
580
|
// src/flexipage/render-wireframe.ts
|
|
484
|
-
function
|
|
581
|
+
function getWireframeRollups(diff) {
|
|
582
|
+
const rollups = /* @__PURE__ */ new Map();
|
|
583
|
+
const addEntry = (root, group, id) => {
|
|
584
|
+
const rollup = rollups.get(root) ?? { itemIds: [], groups: /* @__PURE__ */ new Map() };
|
|
585
|
+
rollup.itemIds.push(id);
|
|
586
|
+
const groupItems = rollup.groups.get(group) ?? [];
|
|
587
|
+
groupItems.push(id);
|
|
588
|
+
rollup.groups.set(group, groupItems);
|
|
589
|
+
rollups.set(root, rollup);
|
|
590
|
+
};
|
|
591
|
+
for (const region of diff.regions) {
|
|
592
|
+
if (!region.changes?.length) continue;
|
|
593
|
+
const segments = region.path.split(" \u203A ");
|
|
594
|
+
const root = segments[0];
|
|
595
|
+
addEntry(root, digestGroupLabel([...segments, "__region__"], root), regionChangeId(region));
|
|
596
|
+
}
|
|
597
|
+
for (const item of diff.regions.flatMap((region) => region.items)) {
|
|
598
|
+
if (item.status === "unchanged") continue;
|
|
599
|
+
const segments = item.path.split(" \u203A ");
|
|
600
|
+
const root = segments[0];
|
|
601
|
+
addEntry(root, digestGroupLabel(segments, root), item.id);
|
|
602
|
+
}
|
|
603
|
+
return Object.fromEntries([...rollups.entries()].map(([region, value]) => [region, {
|
|
604
|
+
count: value.itemIds.length,
|
|
605
|
+
itemIds: value.itemIds,
|
|
606
|
+
groups: [...value.groups.entries()].map(([label, itemIds]) => ({ label, itemIds }))
|
|
607
|
+
}]));
|
|
608
|
+
}
|
|
609
|
+
function regionChangeId(region) {
|
|
610
|
+
return `region:${region.name}`;
|
|
611
|
+
}
|
|
612
|
+
function renderWireframe(diff, geometry, rollups = getWireframeRollups(diff)) {
|
|
485
613
|
if (!geometry) return void 0;
|
|
486
614
|
validateTemplateGeometry(geometry);
|
|
487
615
|
const slots = geometry.rows.flatMap((row) => row.flatMap(slotsInCell));
|
|
@@ -504,7 +632,9 @@ function renderWireframe(diff, geometry) {
|
|
|
504
632
|
const renderSlot = (slot) => {
|
|
505
633
|
const region = regions.get(slot);
|
|
506
634
|
const status = region?.status ?? "unchanged";
|
|
507
|
-
|
|
635
|
+
const rollup = rollups[slot];
|
|
636
|
+
const rollupMarkup = rollup ? `<button class="wireframe-rollup" type="button" data-rollup-region="${escapeHtml2(slot)}" aria-label="${rollup.count} changes in ${escapeHtml2(slot)}">${rollup.count} ${rollup.count === 1 ? "change" : "changes"}</button>` : "";
|
|
637
|
+
return `<div class="wireframe-cell${region ? ` ${status}` : " wireframe-empty"}" data-region-status="${status}" data-slot="${escapeHtml2(slot)}"><div class="wireframe-slot-head"><span class="status-badge">${status}</span><span>${escapeHtml2(slot)}</span>${rollupMarkup}</div><div class="wireframe-items">${region ? renderRegionItems(region) : '<span class="wireframe-empty-label">Empty slot</span>'}</div></div>`;
|
|
508
638
|
};
|
|
509
639
|
const renderCell = (cell) => {
|
|
510
640
|
if (isStack2(cell)) return `<div class="wireframe-cell wireframe-stack-cell"><div class="wireframe-stack">${cell.stack.map((child) => Array.isArray(child) ? renderNestedRow(child) : renderSlot(child.slot)).join("")}</div></div>`;
|
|
@@ -522,6 +652,10 @@ function renderWireframe(diff, geometry) {
|
|
|
522
652
|
return `<div class="wireframe-removed-region" data-region-status="${region.status}"><div class="wireframe-slot-head"><span class="status-badge">${region.status}</span><span>${escapeHtml2(region.name)}</span></div><div class="wireframe-items">${renderRegionItems(region)}</div></div>`;
|
|
523
653
|
}
|
|
524
654
|
}
|
|
655
|
+
function digestGroupLabel(segments, root) {
|
|
656
|
+
const ancestor = [...segments.slice(1, -1)].reverse().find((segment) => /\([^()]+\)$/.test(segment));
|
|
657
|
+
return ancestor?.match(/\(([^()]+)\)$/)?.[1] ?? segments.at(-2) ?? root;
|
|
658
|
+
}
|
|
525
659
|
function slotsInCell(cell) {
|
|
526
660
|
if (!isStack2(cell)) return [cell.slot];
|
|
527
661
|
return cell.stack.flatMap((child) => Array.isArray(child) ? child.flatMap(slotsInCell) : [child.slot]);
|
|
@@ -567,24 +701,45 @@ function renderOutline(diff, options = {}) {
|
|
|
567
701
|
const metaHtml = `<span class="summary-stat added">+${summary.addedComponents} components</span><span class="summary-stat deleted">\u2212${summary.removedComponents} components</span><span class="summary-stat modified">~${summary.modifiedComponents} components</span><span class="summary-stat">+${summary.addedRegions}/\u2212${summary.removedRegions} regions</span><span class="summary-stat">${summary.changedPageAttributes} page attributes</span>`;
|
|
568
702
|
const templateChange = diff.pageChanges?.find((change) => change.path === "template");
|
|
569
703
|
const bannerHtml = templateChange ? `<div class="banner">Template changed: ${renderInlineValue(templateChange.before)} \u2192 ${renderInlineValue(templateChange.after)}</div>` : "";
|
|
570
|
-
const data = Object.fromEntries(
|
|
571
|
-
|
|
704
|
+
const data = Object.fromEntries([
|
|
705
|
+
...diff.regions.flatMap((region) => region.items.map((item) => [item.id, item])),
|
|
706
|
+
...diff.regions.flatMap((region) => region.changes?.length ? [[regionChangeId(region), {
|
|
707
|
+
id: regionChangeId(region),
|
|
708
|
+
path: region.path,
|
|
709
|
+
kind: "region",
|
|
710
|
+
status: region.status,
|
|
711
|
+
componentName: region.name.split(" \u203A ").at(-1)?.match(/\(([^()]+)\)$/)?.[1] ?? region.name.split(" \u203A ").at(-1) ?? region.name,
|
|
712
|
+
changes: region.changes
|
|
713
|
+
}]] : [])
|
|
714
|
+
]);
|
|
715
|
+
const rollups = getWireframeRollups(diff);
|
|
716
|
+
const json = JSON.stringify({ items: data, rollups }).replace(/</g, "\\u003c");
|
|
572
717
|
const clientScript = `const DATA = ${json};
|
|
573
718
|
const rows = [...document.querySelectorAll('.outline-row')];
|
|
574
719
|
const regions = [...document.querySelectorAll('[data-region-status]')];
|
|
575
720
|
const filters = [...document.querySelectorAll('.filter-button')];
|
|
576
|
-
|
|
721
|
+
const panelTitle = document.getElementById('panel-title');
|
|
722
|
+
const panelBadge = document.getElementById('panel-badge');
|
|
723
|
+
const panelBody = document.getElementById('panel-body');
|
|
724
|
+
const panelBack = document.getElementById('panel-back');
|
|
725
|
+
let activeDigestRegion;
|
|
726
|
+
function changed(el) { return el.dataset.regionStatus !== 'unchanged' || el.querySelector('.outline-row:not(.unchanged)') !== null; }
|
|
577
727
|
function applyView(mode) {
|
|
578
728
|
filters.forEach((button) => button.classList.toggle('active', button.dataset.viewMode === mode));
|
|
579
729
|
rows.forEach((row) => { const status = row.dataset.status; row.hidden = mode === 'after' ? status === 'deleted' : mode === 'before' ? status === 'added' : mode === 'changes' ? status === 'unchanged' : false; });
|
|
580
730
|
regions.forEach((region) => { const wireframeCell = region.classList.contains('wireframe-cell'); region.hidden = mode === 'after' ? region.dataset.regionStatus === 'deleted' : mode === 'before' ? region.dataset.regionStatus === 'added' : mode === 'changes' ? !wireframeCell && !changed(region) : false; });
|
|
581
731
|
}
|
|
582
732
|
filters.forEach((button) => button.addEventListener('click', () => applyView(button.dataset.viewMode)));
|
|
583
|
-
function text(value) { return value === undefined ? '(missing)' : String(value); }
|
|
733
|
+
function text(value) { return value === undefined ? '(missing)' : typeof value === 'object' ? JSON.stringify(value) : String(value); }
|
|
584
734
|
function escape(value) { return text(value).replaceAll('&','&').replaceAll('<','<').replaceAll('>','>').replaceAll('"','"'); }
|
|
585
735
|
function renderValue(before, after) { if (before === undefined) return '<span class="val after">' + escape(after) + '</span>'; if (after === undefined) return '<span class="val before">' + escape(before) + '</span>'; return '<span class="val before">' + escape(before) + '</span><span class="arrow">\u2192</span><span class="val after">' + escape(after) + '</span>'; }
|
|
586
|
-
function
|
|
736
|
+
function selectItem(id, fromDigest = false) { const item = DATA.items[id]; if (!item) return; if (!fromDigest) { activeDigestRegion = undefined; panelBack.hidden = true; } else { panelBack.hidden = false; panelBack.textContent = 'Back to ' + activeDigestRegion + ' changes'; } const title = item.componentName || item.fieldItem || 'Item'; panelTitle.textContent = title; panelBadge.hidden = false; panelBadge.textContent = item.status; panelBadge.className = 'panel-badge ' + item.status; const location = item.path ? '<div class="item-path">' + escape(item.path) + '</div>' : ''; const notes = item.notes?.length ? '<ul class="item-notes">' + item.notes.map((note) => '<li>' + escape(note) + '</li>').join('') + '</ul>' : ''; if (!item.changes || item.changes.length === 0) { if (item.status === 'unchanged' && !notes) { panelBody.className = 'empty'; panelBody.textContent = 'No property changes.'; } else { panelBody.className = 'empty'; panelBody.innerHTML = location + notes + '<div>' + (item.status === 'unchanged' ? 'No property changes.' : 'No property details available.') + '</div>'; } return; } panelBody.className = ''; panelBody.innerHTML = location + notes + '<ul class="changes">' + item.changes.map((change) => '<li><span class="change-path">' + escape(change.path) + '</span>' + renderValue(change.before, change.after) + '</li>').join('') + '</ul>'; }
|
|
737
|
+
function selectRow(row) { selectItem(row.dataset.itemId); }
|
|
738
|
+
function digestEntry(id) { const item = DATA.items[id]; if (!item) return ''; const label = item.componentName || item.fieldItem || 'Item'; return '<button class="digest-entry ' + item.status + '" type="button" data-digest-item-id="' + escape(id) + '"><span class="status-badge ' + item.status + '">' + escape(item.status) + '</span><span>' + escape(label) + '</span></button>'; }
|
|
739
|
+
function showDigest(regionName) { const rollup = DATA.rollups[regionName]; if (!rollup) return; document.body.classList.remove('panel-collapsed'); document.getElementById('panel-toggle')?.setAttribute('aria-expanded','true'); activeDigestRegion = regionName; panelTitle.textContent = regionName + ' changes'; panelBadge.hidden = true; panelBack.hidden = true; panelBody.className = ''; panelBody.innerHTML = '<div class="digest">' + rollup.groups.map((group) => '<section class="digest-group"><div class="digest-group-head"><span>' + escape(group.label) + '</span><span class="digest-group-count">' + group.itemIds.length + ' ' + (group.itemIds.length === 1 ? 'change' : 'changes') + '</span></div>' + group.itemIds.map(digestEntry).join('') + '</section>').join('') + '</div>'; panelBody.querySelectorAll('[data-digest-item-id]').forEach((entry) => entry.addEventListener('click', () => selectItem(entry.dataset.digestItemId, true))); }
|
|
587
740
|
rows.forEach((row) => { row.addEventListener('click', () => selectRow(row)); row.addEventListener('keydown', (event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); selectRow(row); } }); });
|
|
741
|
+
document.querySelectorAll('.wireframe-rollup').forEach((button) => button.addEventListener('click', (event) => { event.stopPropagation(); showDigest(button.dataset.rollupRegion); }));
|
|
742
|
+
panelBack.addEventListener('click', () => { if (activeDigestRegion) showDigest(activeDigestRegion); });
|
|
588
743
|
const themeButtons = [...document.querySelectorAll('.theme-button')];
|
|
589
744
|
function applyTheme(theme) { document.documentElement.dataset.theme = theme; themeButtons.forEach((button) => button.setAttribute('aria-pressed', button.dataset.themeChoice === theme ? 'true' : 'false')); }
|
|
590
745
|
function storedTheme() { try { const value = localStorage.getItem(${JSON.stringify("flow-delta-theme")}); return ['system','light','dark'].includes(value) ? value : 'system'; } catch { return 'system'; } }
|
|
@@ -592,7 +747,7 @@ function renderOutline(diff, options = {}) {
|
|
|
592
747
|
applyTheme(storedTheme()); applyView('all');
|
|
593
748
|
const panelToggle = document.getElementById('panel-toggle'); const panelReopen = document.getElementById('panel-reopen'); panelToggle.addEventListener('click', () => { document.body.classList.add('panel-collapsed'); panelToggle.setAttribute('aria-expanded','false'); }); panelReopen.addEventListener('click', () => { document.body.classList.remove('panel-collapsed'); panelToggle.setAttribute('aria-expanded','true'); });
|
|
594
749
|
const resizer = document.getElementById('panel-resizer'); let resizeStart; resizer.addEventListener('pointerdown', (event) => { resizeStart = { x:event.clientX, width:document.getElementById('detail-panel').getBoundingClientRect().width }; resizer.setPointerCapture(event.pointerId); }); resizer.addEventListener('pointermove', (event) => { if (!resizeStart) return; document.documentElement.style.setProperty('--panel-width', Math.max(300, resizeStart.width - (event.clientX - resizeStart.x)) + 'px'); }); resizer.addEventListener('pointerup', () => { resizeStart = undefined; });`;
|
|
595
|
-
const wireframe = renderWireframe(diff, getTemplateGeometry(options.template));
|
|
750
|
+
const wireframe = renderWireframe(diff, getTemplateGeometry(options.template), rollups);
|
|
596
751
|
return renderShell({ title: diff.pageName, productName: "FlexiPageDelta", metaHtml, bannerHtml, contentHtml: content, wireframeHtml: wireframe ?? void 0, defaultView: wireframe ? "wireframe" : "outline", clientScript });
|
|
597
752
|
}
|
|
598
753
|
function renderInlineValue(value) {
|