@dbx-tools/cli 0.9.139 → 0.9.141

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.
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Self-contained HTML presentation for an exported brand package.
3
+ *
4
+ * @module
5
+ */
6
+ import { stringUtils } from "@dbx-tools/shared-core";
7
+ function safeAsset(value) {
8
+ if (!value)
9
+ return undefined;
10
+ if (/^(?:data:image\/|https:\/\/)/i.test(value))
11
+ return value;
12
+ return /^\.{0,2}\//.test(value) ? value : undefined;
13
+ }
14
+ function safeWebsite(value) {
15
+ return value && /^https:\/\//i.test(value) ? value : undefined;
16
+ }
17
+ function safeFontFamily(value) {
18
+ return /^[\w\s'",.-]+$/.test(value) ? value : "ui-sans-serif, system-ui, sans-serif";
19
+ }
20
+ function listItems(values) {
21
+ return values.map((value) => `<li>${stringUtils.escapeHtml(value)}</li>`).join("");
22
+ }
23
+ /** Render one responsive brand presentation with desktop and mobile mockups. */
24
+ export function renderBrandShowcase(brandPackage) {
25
+ const { brand, visualReferences } = brandPackage;
26
+ const html = stringUtils.escapeHtml;
27
+ const logo = safeAsset(brand.assets.logo.light) ?? safeAsset(brand.assets.icon.light);
28
+ const icon = safeAsset(brand.assets.icon.light);
29
+ const website = safeWebsite(brand.links.website);
30
+ const screenshots = visualReferences
31
+ .flatMap((reference) => reference.images.flatMap((image) => image.localPath
32
+ ? [{ appName: reference.appName, platform: reference.platform, path: image.localPath }]
33
+ : []))
34
+ .slice(0, 4);
35
+ const identityMark = logo
36
+ ? `<img src="${html(logo)}" alt="${html(brand.name)} logo">`
37
+ : `<span class="wordmark">${html(brand.shortName)}</span>`;
38
+ const compactMark = icon
39
+ ? `<img src="${html(icon)}" alt="" aria-hidden="true">`
40
+ : `<span class="compact-wordmark">${html(brand.shortName.slice(0, 2))}</span>`;
41
+ const action = website
42
+ ? `<a class="button" href="${html(website)}">Visit website</a>`
43
+ : `<span class="button">Explore the brand</span>`;
44
+ const gallery = screenshots.length
45
+ ? `
46
+ <section class="references" aria-labelledby="references-title">
47
+ <div class="section-heading">
48
+ <h2 id="references-title">Visual references</h2>
49
+ <p>Supplemental mobile references used during research.</p>
50
+ </div>
51
+ <div class="reference-grid">
52
+ ${screenshots
53
+ .map(({ appName, path, platform }) => `
54
+ <figure>
55
+ <img src="${html(path)}" alt="${html(appName)} ${html(platform)} reference">
56
+ <figcaption>${html(appName)} / ${html(platform)}</figcaption>
57
+ </figure>
58
+ `)
59
+ .join("")}
60
+ </div>
61
+ </section>
62
+ `
63
+ : "";
64
+ const headline = brand.tagline ?? brand.name;
65
+ // prettier-ignore
66
+ return stringUtils.dedent(
67
+ // ============================================================================
68
+ /*html*/ `
69
+ <!doctype html>
70
+ <html lang="en">
71
+ <head>
72
+ <meta charset="utf-8">
73
+ <meta name="viewport" content="width=device-width, initial-scale=1">
74
+ <title>${html(brand.name)} brand presentation</title>
75
+ <style>
76
+ :root {
77
+ color-scheme: light;
78
+ --primary: ${brand.colors.primary};
79
+ --primary-hover: ${brand.colors.primaryHover};
80
+ --accent: ${brand.colors.accent};
81
+ --foreground: ${brand.colors.foreground};
82
+ --background: ${brand.colors.background};
83
+ --surface: ${brand.colors.surface};
84
+ --muted: ${brand.colors.muted};
85
+ --border: ${brand.colors.border};
86
+ --sans: ${safeFontFamily(brand.typography.sans)};
87
+ --mono: ${safeFontFamily(brand.typography.mono)};
88
+ }
89
+ * { box-sizing: border-box; }
90
+ body {
91
+ margin: 0;
92
+ background: var(--background);
93
+ color: var(--foreground);
94
+ font-family: var(--sans);
95
+ }
96
+ a { color: inherit; }
97
+ img { display: block; max-width: 100%; }
98
+ main { margin: 0 auto; max-width: 1240px; padding: 0 32px 72px; }
99
+ .masthead {
100
+ align-items: center;
101
+ border-bottom: 1px solid var(--border);
102
+ display: flex;
103
+ justify-content: space-between;
104
+ min-height: 88px;
105
+ }
106
+ .masthead img { max-height: 42px; max-width: 210px; }
107
+ .wordmark { font-size: 24px; font-weight: 750; letter-spacing: -0.04em; }
108
+ .meta { color: var(--muted); font-family: var(--mono); font-size: 12px; }
109
+ .identity {
110
+ display: grid;
111
+ gap: 48px;
112
+ grid-template-columns: minmax(0, 1.4fr) minmax(260px, 0.6fr);
113
+ padding: 72px 0 64px;
114
+ }
115
+ .identity h1 {
116
+ font-size: clamp(48px, 8vw, 104px);
117
+ letter-spacing: -0.065em;
118
+ line-height: 0.92;
119
+ margin: 0;
120
+ max-width: 850px;
121
+ }
122
+ .identity-copy { align-self: end; }
123
+ .identity-copy p { color: var(--muted); font-size: 17px; line-height: 1.65; margin: 0 0 28px; }
124
+ .button {
125
+ background: var(--primary);
126
+ border-radius: 999px;
127
+ color: var(--background);
128
+ display: inline-flex;
129
+ font-weight: 700;
130
+ padding: 12px 20px;
131
+ text-decoration: none;
132
+ }
133
+ .showcase {
134
+ background: var(--surface);
135
+ border: 1px solid var(--border);
136
+ border-radius: 28px;
137
+ overflow: hidden;
138
+ padding: 38px;
139
+ }
140
+ .section-heading { align-items: end; display: flex; gap: 24px; justify-content: space-between; margin-bottom: 28px; }
141
+ .section-heading h2 { font-size: 30px; letter-spacing: -0.035em; margin: 0; }
142
+ .section-heading p { color: var(--muted); margin: 0; max-width: 460px; text-align: right; }
143
+ .devices { align-items: end; display: grid; gap: 28px; grid-template-columns: minmax(0, 1fr) 280px; }
144
+ .browser, .phone {
145
+ background: var(--background);
146
+ border: 1px solid color-mix(in srgb, var(--foreground) 18%, transparent);
147
+ box-shadow: 0 24px 70px color-mix(in srgb, var(--foreground) 13%, transparent);
148
+ overflow: hidden;
149
+ }
150
+ .browser { border-radius: 18px; }
151
+ .browser-bar { align-items: center; background: var(--foreground); display: flex; gap: 7px; height: 38px; padding: 0 16px; }
152
+ .browser-bar i { background: var(--accent); border-radius: 50%; display: block; height: 7px; opacity: 0.8; width: 7px; }
153
+ .browser-nav, .phone-nav { align-items: center; display: flex; justify-content: space-between; padding: 20px 24px; }
154
+ .browser-nav img { max-height: 30px; max-width: 150px; }
155
+ .nav-links { color: var(--muted); display: flex; font-size: 12px; gap: 22px; }
156
+ .mock-hero { display: grid; gap: 28px; grid-template-columns: 1.1fr 0.9fr; min-height: 350px; padding: 44px; }
157
+ .mock-copy { align-self: center; }
158
+ .mock-copy h3 { font-size: clamp(34px, 5vw, 64px); letter-spacing: -0.055em; line-height: 0.98; margin: 0 0 22px; }
159
+ .mock-copy p { color: var(--muted); line-height: 1.55; margin: 0 0 24px; max-width: 520px; }
160
+ .mock-visual {
161
+ align-content: end;
162
+ background: var(--primary);
163
+ border-radius: 20px;
164
+ color: var(--background);
165
+ display: grid;
166
+ min-height: 250px;
167
+ overflow: hidden;
168
+ padding: 28px;
169
+ position: relative;
170
+ }
171
+ .mock-visual::before {
172
+ background: var(--accent);
173
+ border-radius: 50%;
174
+ content: "";
175
+ height: 210px;
176
+ position: absolute;
177
+ right: -70px;
178
+ top: -80px;
179
+ width: 210px;
180
+ }
181
+ .mock-visual strong { font-size: 22px; line-height: 1.15; max-width: 220px; position: relative; }
182
+ .phone { border: 8px solid var(--foreground); border-radius: 34px; }
183
+ .phone-screen { min-height: 540px; }
184
+ .phone-nav { padding: 18px; }
185
+ .phone-nav img { height: 30px; max-width: 112px; object-fit: contain; object-position: left center; }
186
+ .phone-mark img { height: 30px; width: 30px; }
187
+ .compact-wordmark { font-weight: 800; }
188
+ .phone-copy { padding: 34px 22px 24px; }
189
+ .phone-copy h3 { font-size: 38px; letter-spacing: -0.05em; line-height: 0.98; margin: 0 0 18px; }
190
+ .phone-copy p { color: var(--muted); font-size: 14px; line-height: 1.55; margin: 0; }
191
+ .phone-accent { background: var(--accent); height: 120px; margin: 18px 22px 22px; }
192
+ .system {
193
+ display: grid;
194
+ gap: 48px;
195
+ grid-template-columns: 1fr 1fr;
196
+ padding: 72px 0;
197
+ }
198
+ .palette { display: grid; grid-template-columns: repeat(4, 1fr); min-height: 250px; }
199
+ .swatch { align-items: end; display: flex; padding: 16px; }
200
+ .swatch span { background: var(--background); color: var(--foreground); font-family: var(--mono); font-size: 11px; padding: 5px 7px; }
201
+ .type-specimen { border-top: 1px solid var(--border); padding-top: 24px; }
202
+ .type-specimen strong { display: block; font-size: clamp(44px, 6vw, 76px); letter-spacing: -0.05em; line-height: 0.95; }
203
+ .type-specimen code { color: var(--muted); display: block; font-family: var(--mono); margin-top: 20px; }
204
+ .voice { border-top: 1px solid var(--border); display: grid; gap: 32px; grid-template-columns: 1fr 1fr; padding: 36px 0; }
205
+ .voice h2 { font-size: 30px; letter-spacing: -0.035em; margin: 0; }
206
+ .voice h3 { font-size: 14px; margin: 0 0 12px; }
207
+ .voice ul { color: var(--muted); line-height: 1.6; margin: 0; padding-left: 18px; }
208
+ .references { border-top: 1px solid var(--border); padding-top: 56px; }
209
+ .reference-grid { display: grid; gap: 18px; grid-template-columns: repeat(4, 1fr); }
210
+ figure { margin: 0; }
211
+ figure img { aspect-ratio: 9 / 16; background: var(--surface); border: 1px solid var(--border); object-fit: contain; width: 100%; }
212
+ figcaption { color: var(--muted); font-size: 11px; margin-top: 8px; }
213
+ @media (max-width: 860px) {
214
+ main { padding: 0 18px 48px; }
215
+ .identity, .system { grid-template-columns: 1fr; }
216
+ .identity { gap: 28px; padding: 52px 0 44px; }
217
+ .showcase { border-radius: 20px; padding: 20px; }
218
+ .devices { grid-template-columns: 1fr; }
219
+ .phone { margin: 0 auto; max-width: 320px; width: 100%; }
220
+ .reference-grid { grid-template-columns: repeat(2, 1fr); }
221
+ }
222
+ @media (max-width: 560px) {
223
+ .masthead { min-height: 72px; }
224
+ .identity h1 { font-size: 52px; }
225
+ .section-heading { align-items: start; flex-direction: column; }
226
+ .section-heading p { text-align: left; }
227
+ .mock-hero { grid-template-columns: 1fr; padding: 24px; }
228
+ .mock-visual { min-height: 190px; }
229
+ .nav-links { display: none; }
230
+ .palette { grid-template-columns: 1fr 1fr; }
231
+ .voice { grid-template-columns: 1fr; }
232
+ }
233
+ @media (prefers-reduced-motion: no-preference) {
234
+ .button { transition: background-color 160ms ease, transform 160ms ease; }
235
+ .button:hover { background: var(--primary-hover); transform: translateY(-1px); }
236
+ }
237
+ .button:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
238
+ </style>
239
+ </head>
240
+ <body>
241
+ <main>
242
+ <header class="masthead">
243
+ ${identityMark}
244
+ <span class="meta">Brand system / ${html(brand.schemaVersion)}</span>
245
+ </header>
246
+
247
+ <section class="identity" aria-labelledby="brand-name">
248
+ <h1 id="brand-name">${html(brand.name)}</h1>
249
+ <div class="identity-copy">
250
+ <p>${html(brand.description)}</p>
251
+ ${action}
252
+ </div>
253
+ </section>
254
+
255
+ <section class="showcase" aria-labelledby="mockup-title">
256
+ <div class="section-heading">
257
+ <h2 id="mockup-title">Website mockups</h2>
258
+ <p>One responsive direction shown at desktop and mobile sizes.</p>
259
+ </div>
260
+ <div class="devices">
261
+ <article class="browser" aria-label="Desktop website mockup">
262
+ <div class="browser-bar"><i></i><i></i><i></i></div>
263
+ <div class="browser-nav">
264
+ ${identityMark}
265
+ <div class="nav-links"><span>Overview</span><span>Product</span><span>Resources</span></div>
266
+ </div>
267
+ <div class="mock-hero">
268
+ <div class="mock-copy">
269
+ <h3>${html(headline)}</h3>
270
+ <p>${html(brand.description)}</p>
271
+ ${action}
272
+ </div>
273
+ <div class="mock-visual">
274
+ <strong>${html(brand.voice.principles[0] ?? brand.name)}</strong>
275
+ </div>
276
+ </div>
277
+ </article>
278
+
279
+ <article class="phone" aria-label="Mobile website mockup">
280
+ <div class="phone-screen">
281
+ <div class="phone-nav">
282
+ <span class="phone-mark">${compactMark}</span>
283
+ <span class="meta">Menu</span>
284
+ </div>
285
+ <div class="phone-copy">
286
+ <h3>${html(headline)}</h3>
287
+ <p>${html(brand.description)}</p>
288
+ </div>
289
+ <div class="phone-accent"></div>
290
+ </div>
291
+ </article>
292
+ </div>
293
+ </section>
294
+
295
+ <section class="system" aria-label="Brand system">
296
+ <div>
297
+ <div class="section-heading"><h2>Core palette</h2></div>
298
+ <div class="palette">
299
+ <div class="swatch" style="background: var(--primary)"><span>${brand.colors.primary}</span></div>
300
+ <div class="swatch" style="background: var(--accent)"><span>${brand.colors.accent}</span></div>
301
+ <div class="swatch" style="background: var(--surface)"><span>${brand.colors.surface}</span></div>
302
+ <div class="swatch" style="background: var(--foreground)"><span>${brand.colors.foreground}</span></div>
303
+ </div>
304
+ </div>
305
+ <div>
306
+ <div class="section-heading"><h2>Typography</h2></div>
307
+ <div class="type-specimen">
308
+ <strong>Aa 01</strong>
309
+ <code>${html(brand.typography.sans)}</code>
310
+ </div>
311
+ </div>
312
+ </section>
313
+
314
+ <section class="voice" aria-labelledby="voice-title">
315
+ <div>
316
+ <h2 id="voice-title">Voice</h2>
317
+ <p class="meta">${html(brand.voice.tone.join(", "))}</p>
318
+ </div>
319
+ <div>
320
+ <h3>Principles</h3>
321
+ <ul>${listItems(brand.voice.principles)}</ul>
322
+ </div>
323
+ </section>
324
+
325
+ ${gallery}
326
+ </main>
327
+ </body>
328
+ </html>
329
+ `
330
+ // ============================================================================
331
+ );
332
+ }
333
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2hvd2Nhc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYnJhbmQvc2hvd2Nhc2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRztBQUVILE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUdyRCxTQUFTLFNBQVMsQ0FBQyxLQUF5QjtJQUMxQyxJQUFJLENBQUMsS0FBSztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQzdCLElBQUksK0JBQStCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBQzlELE9BQU8sWUFBWSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUFDdEQsQ0FBQztBQUVELFNBQVMsV0FBVyxDQUFDLEtBQXlCO0lBQzVDLE9BQU8sS0FBSyxJQUFJLGNBQWMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0FBQ2pFLENBQUM7QUFFRCxTQUFTLGNBQWMsQ0FBQyxLQUFhO0lBQ25DLE9BQU8sZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLHNDQUFzQyxDQUFDO0FBQ3ZGLENBQUM7QUFFRCxTQUFTLFNBQVMsQ0FBQyxNQUF5QjtJQUMxQyxPQUFPLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLE9BQU8sV0FBVyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQ3JGLENBQUM7QUFFRCxnRkFBZ0Y7QUFDaEYsTUFBTSxVQUFVLG1CQUFtQixDQUFDLFlBQTBCO0lBQzVELE1BQU0sRUFBRSxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsR0FBRyxZQUFZLENBQUM7SUFDakQsTUFBTSxJQUFJLEdBQUcsV0FBVyxDQUFDLFVBQVUsQ0FBQztJQUNwQyxNQUFNLElBQUksR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksU0FBUyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3RGLE1BQU0sSUFBSSxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNoRCxNQUFNLE9BQU8sR0FBRyxXQUFXLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNqRCxNQUFNLFdBQVcsR0FBRyxnQkFBZ0I7U0FDakMsT0FBTyxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FDckIsU0FBUyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUNqQyxLQUFLLENBQUMsU0FBUztRQUNiLENBQUMsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsUUFBUSxFQUFFLFNBQVMsQ0FBQyxRQUFRLEVBQUUsSUFBSSxFQUFFLEtBQUssQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUN2RixDQUFDLENBQUMsRUFBRSxDQUNQLENBQ0Y7U0FDQSxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ2YsTUFBTSxZQUFZLEdBQUcsSUFBSTtRQUN2QixDQUFDLENBQUMsYUFBYSxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUztRQUM1RCxDQUFDLENBQUMsMEJBQTBCLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQztJQUM3RCxNQUFNLFdBQVcsR0FBRyxJQUFJO1FBQ3RCLENBQUMsQ0FBQyxhQUFhLElBQUksQ0FBQyxJQUFJLENBQUMsOEJBQThCO1FBQ3ZELENBQUMsQ0FBQyxrQ0FBa0MsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDakYsTUFBTSxNQUFNLEdBQUcsT0FBTztRQUNwQixDQUFDLENBQUMsMkJBQTJCLElBQUksQ0FBQyxPQUFPLENBQUMscUJBQXFCO1FBQy9ELENBQUMsQ0FBQywrQ0FBK0MsQ0FBQztJQUNwRCxNQUFNLE9BQU8sR0FBRyxXQUFXLENBQUMsTUFBTTtRQUNoQyxDQUFDLENBQUM7Ozs7Ozs7WUFPTSxXQUFXO2FBQ1YsR0FBRyxDQUNGLENBQUMsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsQ0FBQzs7OEJBRWpCLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQztnQ0FDakQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUM7O2VBRWxELENBQ0Y7YUFDQSxJQUFJLENBQUMsRUFBRSxDQUFDOzs7S0FHaEI7UUFDRCxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQ1AsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLE9BQU8sSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDO0lBRTdDLGtCQUFrQjtJQUNsQixPQUFPLFdBQVcsQ0FBQyxNQUFNO0lBQ3ZCLCtFQUErRTtJQUMvRSxRQUFRLENBQUE7Ozs7OztpQkFNSyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQzs7Ozt5QkFJUixLQUFLLENBQUMsTUFBTSxDQUFDLE9BQU87K0JBQ2QsS0FBSyxDQUFDLE1BQU0sQ0FBQyxZQUFZO3dCQUNoQyxLQUFLLENBQUMsTUFBTSxDQUFDLE1BQU07NEJBQ2YsS0FBSyxDQUFDLE1BQU0sQ0FBQyxVQUFVOzRCQUN2QixLQUFLLENBQUMsTUFBTSxDQUFDLFVBQVU7eUJBQzFCLEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBTzt1QkFDdEIsS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLO3dCQUNqQixLQUFLLENBQUMsTUFBTSxDQUFDLE1BQU07c0JBQ3JCLGNBQWMsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQztzQkFDckMsY0FBYyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Y0E0SjdDLFlBQVk7Z0RBQ3NCLElBQUksQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDOzs7O2tDQUl2QyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQzs7bUJBRS9CLElBQUksQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDO2dCQUMxQixNQUFNOzs7Ozs7Ozs7Ozs7O29CQWFGLFlBQVk7Ozs7OzBCQUtOLElBQUksQ0FBQyxRQUFRLENBQUM7eUJBQ2YsSUFBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUM7c0JBQzFCLE1BQU07Ozs4QkFHRSxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksQ0FBQzs7Ozs7Ozs7K0NBUTVCLFdBQVc7Ozs7MEJBSWhDLElBQUksQ0FBQyxRQUFRLENBQUM7eUJBQ2YsSUFBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUM7Ozs7Ozs7Ozs7OzsrRUFZK0IsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPOzhFQUNyQixLQUFLLENBQUMsTUFBTSxDQUFDLE1BQU07K0VBQ2xCLEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBTztrRkFDakIsS0FBSyxDQUFDLE1BQU0sQ0FBQyxVQUFVOzs7Ozs7O3dCQU9qRixJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUM7Ozs7Ozs7O2dDQVFuQixJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDOzs7O29CQUk3QyxTQUFTLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUM7Ozs7WUFJekMsT0FBTzs7OztLQUlkO0lBQ0QsK0VBQStFO0tBQ2hGLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBTZWxmLWNvbnRhaW5lZCBIVE1MIHByZXNlbnRhdGlvbiBmb3IgYW4gZXhwb3J0ZWQgYnJhbmQgcGFja2FnZS5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgc3RyaW5nVXRpbHMgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHR5cGUgeyBCcmFuZFBhY2thZ2UgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtZ2VuZXJhdG9yXCI7XG5cbmZ1bmN0aW9uIHNhZmVBc3NldCh2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgaWYgKCF2YWx1ZSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgaWYgKC9eKD86ZGF0YTppbWFnZVxcL3xodHRwczpcXC9cXC8pL2kudGVzdCh2YWx1ZSkpIHJldHVybiB2YWx1ZTtcbiAgcmV0dXJuIC9eXFwuezAsMn1cXC8vLnRlc3QodmFsdWUpID8gdmFsdWUgOiB1bmRlZmluZWQ7XG59XG5cbmZ1bmN0aW9uIHNhZmVXZWJzaXRlKHZhbHVlOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICByZXR1cm4gdmFsdWUgJiYgL15odHRwczpcXC9cXC8vaS50ZXN0KHZhbHVlKSA/IHZhbHVlIDogdW5kZWZpbmVkO1xufVxuXG5mdW5jdGlvbiBzYWZlRm9udEZhbWlseSh2YWx1ZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIC9eW1xcd1xccydcIiwuLV0rJC8udGVzdCh2YWx1ZSkgPyB2YWx1ZSA6IFwidWktc2Fucy1zZXJpZiwgc3lzdGVtLXVpLCBzYW5zLXNlcmlmXCI7XG59XG5cbmZ1bmN0aW9uIGxpc3RJdGVtcyh2YWx1ZXM6IHJlYWRvbmx5IHN0cmluZ1tdKTogc3RyaW5nIHtcbiAgcmV0dXJuIHZhbHVlcy5tYXAoKHZhbHVlKSA9PiBgPGxpPiR7c3RyaW5nVXRpbHMuZXNjYXBlSHRtbCh2YWx1ZSl9PC9saT5gKS5qb2luKFwiXCIpO1xufVxuXG4vKiogUmVuZGVyIG9uZSByZXNwb25zaXZlIGJyYW5kIHByZXNlbnRhdGlvbiB3aXRoIGRlc2t0b3AgYW5kIG1vYmlsZSBtb2NrdXBzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlckJyYW5kU2hvd2Nhc2UoYnJhbmRQYWNrYWdlOiBCcmFuZFBhY2thZ2UpOiBzdHJpbmcge1xuICBjb25zdCB7IGJyYW5kLCB2aXN1YWxSZWZlcmVuY2VzIH0gPSBicmFuZFBhY2thZ2U7XG4gIGNvbnN0IGh0bWwgPSBzdHJpbmdVdGlscy5lc2NhcGVIdG1sO1xuICBjb25zdCBsb2dvID0gc2FmZUFzc2V0KGJyYW5kLmFzc2V0cy5sb2dvLmxpZ2h0KSA/PyBzYWZlQXNzZXQoYnJhbmQuYXNzZXRzLmljb24ubGlnaHQpO1xuICBjb25zdCBpY29uID0gc2FmZUFzc2V0KGJyYW5kLmFzc2V0cy5pY29uLmxpZ2h0KTtcbiAgY29uc3Qgd2Vic2l0ZSA9IHNhZmVXZWJzaXRlKGJyYW5kLmxpbmtzLndlYnNpdGUpO1xuICBjb25zdCBzY3JlZW5zaG90cyA9IHZpc3VhbFJlZmVyZW5jZXNcbiAgICAuZmxhdE1hcCgocmVmZXJlbmNlKSA9PlxuICAgICAgcmVmZXJlbmNlLmltYWdlcy5mbGF0TWFwKChpbWFnZSkgPT5cbiAgICAgICAgaW1hZ2UubG9jYWxQYXRoXG4gICAgICAgICAgPyBbeyBhcHBOYW1lOiByZWZlcmVuY2UuYXBwTmFtZSwgcGxhdGZvcm06IHJlZmVyZW5jZS5wbGF0Zm9ybSwgcGF0aDogaW1hZ2UubG9jYWxQYXRoIH1dXG4gICAgICAgICAgOiBbXSxcbiAgICAgICksXG4gICAgKVxuICAgIC5zbGljZSgwLCA0KTtcbiAgY29uc3QgaWRlbnRpdHlNYXJrID0gbG9nb1xuICAgID8gYDxpbWcgc3JjPVwiJHtodG1sKGxvZ28pfVwiIGFsdD1cIiR7aHRtbChicmFuZC5uYW1lKX0gbG9nb1wiPmBcbiAgICA6IGA8c3BhbiBjbGFzcz1cIndvcmRtYXJrXCI+JHtodG1sKGJyYW5kLnNob3J0TmFtZSl9PC9zcGFuPmA7XG4gIGNvbnN0IGNvbXBhY3RNYXJrID0gaWNvblxuICAgID8gYDxpbWcgc3JjPVwiJHtodG1sKGljb24pfVwiIGFsdD1cIlwiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPmBcbiAgICA6IGA8c3BhbiBjbGFzcz1cImNvbXBhY3Qtd29yZG1hcmtcIj4ke2h0bWwoYnJhbmQuc2hvcnROYW1lLnNsaWNlKDAsIDIpKX08L3NwYW4+YDtcbiAgY29uc3QgYWN0aW9uID0gd2Vic2l0ZVxuICAgID8gYDxhIGNsYXNzPVwiYnV0dG9uXCIgaHJlZj1cIiR7aHRtbCh3ZWJzaXRlKX1cIj5WaXNpdCB3ZWJzaXRlPC9hPmBcbiAgICA6IGA8c3BhbiBjbGFzcz1cImJ1dHRvblwiPkV4cGxvcmUgdGhlIGJyYW5kPC9zcGFuPmA7XG4gIGNvbnN0IGdhbGxlcnkgPSBzY3JlZW5zaG90cy5sZW5ndGhcbiAgICA/IGBcbiAgICAgIDxzZWN0aW9uIGNsYXNzPVwicmVmZXJlbmNlc1wiIGFyaWEtbGFiZWxsZWRieT1cInJlZmVyZW5jZXMtdGl0bGVcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cInNlY3Rpb24taGVhZGluZ1wiPlxuICAgICAgICAgIDxoMiBpZD1cInJlZmVyZW5jZXMtdGl0bGVcIj5WaXN1YWwgcmVmZXJlbmNlczwvaDI+XG4gICAgICAgICAgPHA+U3VwcGxlbWVudGFsIG1vYmlsZSByZWZlcmVuY2VzIHVzZWQgZHVyaW5nIHJlc2VhcmNoLjwvcD5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJyZWZlcmVuY2UtZ3JpZFwiPlxuICAgICAgICAgICR7c2NyZWVuc2hvdHNcbiAgICAgICAgICAgIC5tYXAoXG4gICAgICAgICAgICAgICh7IGFwcE5hbWUsIHBhdGgsIHBsYXRmb3JtIH0pID0+IGBcbiAgICAgICAgICAgICAgICA8ZmlndXJlPlxuICAgICAgICAgICAgICAgICAgPGltZyBzcmM9XCIke2h0bWwocGF0aCl9XCIgYWx0PVwiJHtodG1sKGFwcE5hbWUpfSAke2h0bWwocGxhdGZvcm0pfSByZWZlcmVuY2VcIj5cbiAgICAgICAgICAgICAgICAgIDxmaWdjYXB0aW9uPiR7aHRtbChhcHBOYW1lKX0gLyAke2h0bWwocGxhdGZvcm0pfTwvZmlnY2FwdGlvbj5cbiAgICAgICAgICAgICAgICA8L2ZpZ3VyZT5cbiAgICAgICAgICAgICAgYCxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIC5qb2luKFwiXCIpfVxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvc2VjdGlvbj5cbiAgICBgXG4gICAgOiBcIlwiO1xuICBjb25zdCBoZWFkbGluZSA9IGJyYW5kLnRhZ2xpbmUgPz8gYnJhbmQubmFtZTtcblxuICAvLyBwcmV0dGllci1pZ25vcmVcbiAgcmV0dXJuIHN0cmluZ1V0aWxzLmRlZGVudChcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLypodG1sKi9gXG4gICAgPCFkb2N0eXBlIGh0bWw+XG4gICAgPGh0bWwgbGFuZz1cImVuXCI+XG4gICAgICA8aGVhZD5cbiAgICAgICAgPG1ldGEgY2hhcnNldD1cInV0Zi04XCI+XG4gICAgICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MVwiPlxuICAgICAgICA8dGl0bGU+JHtodG1sKGJyYW5kLm5hbWUpfSBicmFuZCBwcmVzZW50YXRpb248L3RpdGxlPlxuICAgICAgICA8c3R5bGU+XG4gICAgICAgICAgOnJvb3Qge1xuICAgICAgICAgICAgY29sb3Itc2NoZW1lOiBsaWdodDtcbiAgICAgICAgICAgIC0tcHJpbWFyeTogJHticmFuZC5jb2xvcnMucHJpbWFyeX07XG4gICAgICAgICAgICAtLXByaW1hcnktaG92ZXI6ICR7YnJhbmQuY29sb3JzLnByaW1hcnlIb3Zlcn07XG4gICAgICAgICAgICAtLWFjY2VudDogJHticmFuZC5jb2xvcnMuYWNjZW50fTtcbiAgICAgICAgICAgIC0tZm9yZWdyb3VuZDogJHticmFuZC5jb2xvcnMuZm9yZWdyb3VuZH07XG4gICAgICAgICAgICAtLWJhY2tncm91bmQ6ICR7YnJhbmQuY29sb3JzLmJhY2tncm91bmR9O1xuICAgICAgICAgICAgLS1zdXJmYWNlOiAke2JyYW5kLmNvbG9ycy5zdXJmYWNlfTtcbiAgICAgICAgICAgIC0tbXV0ZWQ6ICR7YnJhbmQuY29sb3JzLm11dGVkfTtcbiAgICAgICAgICAgIC0tYm9yZGVyOiAke2JyYW5kLmNvbG9ycy5ib3JkZXJ9O1xuICAgICAgICAgICAgLS1zYW5zOiAke3NhZmVGb250RmFtaWx5KGJyYW5kLnR5cG9ncmFwaHkuc2Fucyl9O1xuICAgICAgICAgICAgLS1tb25vOiAke3NhZmVGb250RmFtaWx5KGJyYW5kLnR5cG9ncmFwaHkubW9ubyl9O1xuICAgICAgICAgIH1cbiAgICAgICAgICAqIHsgYm94LXNpemluZzogYm9yZGVyLWJveDsgfVxuICAgICAgICAgIGJvZHkge1xuICAgICAgICAgICAgbWFyZ2luOiAwO1xuICAgICAgICAgICAgYmFja2dyb3VuZDogdmFyKC0tYmFja2dyb3VuZCk7XG4gICAgICAgICAgICBjb2xvcjogdmFyKC0tZm9yZWdyb3VuZCk7XG4gICAgICAgICAgICBmb250LWZhbWlseTogdmFyKC0tc2Fucyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGEgeyBjb2xvcjogaW5oZXJpdDsgfVxuICAgICAgICAgIGltZyB7IGRpc3BsYXk6IGJsb2NrOyBtYXgtd2lkdGg6IDEwMCU7IH1cbiAgICAgICAgICBtYWluIHsgbWFyZ2luOiAwIGF1dG87IG1heC13aWR0aDogMTI0MHB4OyBwYWRkaW5nOiAwIDMycHggNzJweDsgfVxuICAgICAgICAgIC5tYXN0aGVhZCB7XG4gICAgICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xuICAgICAgICAgICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkIHZhcigtLWJvcmRlcik7XG4gICAgICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xuICAgICAgICAgICAgbWluLWhlaWdodDogODhweDtcbiAgICAgICAgICB9XG4gICAgICAgICAgLm1hc3RoZWFkIGltZyB7IG1heC1oZWlnaHQ6IDQycHg7IG1heC13aWR0aDogMjEwcHg7IH1cbiAgICAgICAgICAud29yZG1hcmsgeyBmb250LXNpemU6IDI0cHg7IGZvbnQtd2VpZ2h0OiA3NTA7IGxldHRlci1zcGFjaW5nOiAtMC4wNGVtOyB9XG4gICAgICAgICAgLm1ldGEgeyBjb2xvcjogdmFyKC0tbXV0ZWQpOyBmb250LWZhbWlseTogdmFyKC0tbW9ubyk7IGZvbnQtc2l6ZTogMTJweDsgfVxuICAgICAgICAgIC5pZGVudGl0eSB7XG4gICAgICAgICAgICBkaXNwbGF5OiBncmlkO1xuICAgICAgICAgICAgZ2FwOiA0OHB4O1xuICAgICAgICAgICAgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiBtaW5tYXgoMCwgMS40ZnIpIG1pbm1heCgyNjBweCwgMC42ZnIpO1xuICAgICAgICAgICAgcGFkZGluZzogNzJweCAwIDY0cHg7XG4gICAgICAgICAgfVxuICAgICAgICAgIC5pZGVudGl0eSBoMSB7XG4gICAgICAgICAgICBmb250LXNpemU6IGNsYW1wKDQ4cHgsIDh2dywgMTA0cHgpO1xuICAgICAgICAgICAgbGV0dGVyLXNwYWNpbmc6IC0wLjA2NWVtO1xuICAgICAgICAgICAgbGluZS1oZWlnaHQ6IDAuOTI7XG4gICAgICAgICAgICBtYXJnaW46IDA7XG4gICAgICAgICAgICBtYXgtd2lkdGg6IDg1MHB4O1xuICAgICAgICAgIH1cbiAgICAgICAgICAuaWRlbnRpdHktY29weSB7IGFsaWduLXNlbGY6IGVuZDsgfVxuICAgICAgICAgIC5pZGVudGl0eS1jb3B5IHAgeyBjb2xvcjogdmFyKC0tbXV0ZWQpOyBmb250LXNpemU6IDE3cHg7IGxpbmUtaGVpZ2h0OiAxLjY1OyBtYXJnaW46IDAgMCAyOHB4OyB9XG4gICAgICAgICAgLmJ1dHRvbiB7XG4gICAgICAgICAgICBiYWNrZ3JvdW5kOiB2YXIoLS1wcmltYXJ5KTtcbiAgICAgICAgICAgIGJvcmRlci1yYWRpdXM6IDk5OXB4O1xuICAgICAgICAgICAgY29sb3I6IHZhcigtLWJhY2tncm91bmQpO1xuICAgICAgICAgICAgZGlzcGxheTogaW5saW5lLWZsZXg7XG4gICAgICAgICAgICBmb250LXdlaWdodDogNzAwO1xuICAgICAgICAgICAgcGFkZGluZzogMTJweCAyMHB4O1xuICAgICAgICAgICAgdGV4dC1kZWNvcmF0aW9uOiBub25lO1xuICAgICAgICAgIH1cbiAgICAgICAgICAuc2hvd2Nhc2Uge1xuICAgICAgICAgICAgYmFja2dyb3VuZDogdmFyKC0tc3VyZmFjZSk7XG4gICAgICAgICAgICBib3JkZXI6IDFweCBzb2xpZCB2YXIoLS1ib3JkZXIpO1xuICAgICAgICAgICAgYm9yZGVyLXJhZGl1czogMjhweDtcbiAgICAgICAgICAgIG92ZXJmbG93OiBoaWRkZW47XG4gICAgICAgICAgICBwYWRkaW5nOiAzOHB4O1xuICAgICAgICAgIH1cbiAgICAgICAgICAuc2VjdGlvbi1oZWFkaW5nIHsgYWxpZ24taXRlbXM6IGVuZDsgZGlzcGxheTogZmxleDsgZ2FwOiAyNHB4OyBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47IG1hcmdpbi1ib3R0b206IDI4cHg7IH1cbiAgICAgICAgICAuc2VjdGlvbi1oZWFkaW5nIGgyIHsgZm9udC1zaXplOiAzMHB4OyBsZXR0ZXItc3BhY2luZzogLTAuMDM1ZW07IG1hcmdpbjogMDsgfVxuICAgICAgICAgIC5zZWN0aW9uLWhlYWRpbmcgcCB7IGNvbG9yOiB2YXIoLS1tdXRlZCk7IG1hcmdpbjogMDsgbWF4LXdpZHRoOiA0NjBweDsgdGV4dC1hbGlnbjogcmlnaHQ7IH1cbiAgICAgICAgICAuZGV2aWNlcyB7IGFsaWduLWl0ZW1zOiBlbmQ7IGRpc3BsYXk6IGdyaWQ7IGdhcDogMjhweDsgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiBtaW5tYXgoMCwgMWZyKSAyODBweDsgfVxuICAgICAgICAgIC5icm93c2VyLCAucGhvbmUge1xuICAgICAgICAgICAgYmFja2dyb3VuZDogdmFyKC0tYmFja2dyb3VuZCk7XG4gICAgICAgICAgICBib3JkZXI6IDFweCBzb2xpZCBjb2xvci1taXgoaW4gc3JnYiwgdmFyKC0tZm9yZWdyb3VuZCkgMTglLCB0cmFuc3BhcmVudCk7XG4gICAgICAgICAgICBib3gtc2hhZG93OiAwIDI0cHggNzBweCBjb2xvci1taXgoaW4gc3JnYiwgdmFyKC0tZm9yZWdyb3VuZCkgMTMlLCB0cmFuc3BhcmVudCk7XG4gICAgICAgICAgICBvdmVyZmxvdzogaGlkZGVuO1xuICAgICAgICAgIH1cbiAgICAgICAgICAuYnJvd3NlciB7IGJvcmRlci1yYWRpdXM6IDE4cHg7IH1cbiAgICAgICAgICAuYnJvd3Nlci1iYXIgeyBhbGlnbi1pdGVtczogY2VudGVyOyBiYWNrZ3JvdW5kOiB2YXIoLS1mb3JlZ3JvdW5kKTsgZGlzcGxheTogZmxleDsgZ2FwOiA3cHg7IGhlaWdodDogMzhweDsgcGFkZGluZzogMCAxNnB4OyB9XG4gICAgICAgICAgLmJyb3dzZXItYmFyIGkgeyBiYWNrZ3JvdW5kOiB2YXIoLS1hY2NlbnQpOyBib3JkZXItcmFkaXVzOiA1MCU7IGRpc3BsYXk6IGJsb2NrOyBoZWlnaHQ6IDdweDsgb3BhY2l0eTogMC44OyB3aWR0aDogN3B4OyB9XG4gICAgICAgICAgLmJyb3dzZXItbmF2LCAucGhvbmUtbmF2IHsgYWxpZ24taXRlbXM6IGNlbnRlcjsgZGlzcGxheTogZmxleDsganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuOyBwYWRkaW5nOiAyMHB4IDI0cHg7IH1cbiAgICAgICAgICAuYnJvd3Nlci1uYXYgaW1nIHsgbWF4LWhlaWdodDogMzBweDsgbWF4LXdpZHRoOiAxNTBweDsgfVxuICAgICAgICAgIC5uYXYtbGlua3MgeyBjb2xvcjogdmFyKC0tbXV0ZWQpOyBkaXNwbGF5OiBmbGV4OyBmb250LXNpemU6IDEycHg7IGdhcDogMjJweDsgfVxuICAgICAgICAgIC5tb2NrLWhlcm8geyBkaXNwbGF5OiBncmlkOyBnYXA6IDI4cHg7IGdyaWQtdGVtcGxhdGUtY29sdW1uczogMS4xZnIgMC45ZnI7IG1pbi1oZWlnaHQ6IDM1MHB4OyBwYWRkaW5nOiA0NHB4OyB9XG4gICAgICAgICAgLm1vY2stY29weSB7IGFsaWduLXNlbGY6IGNlbnRlcjsgfVxuICAgICAgICAgIC5tb2NrLWNvcHkgaDMgeyBmb250LXNpemU6IGNsYW1wKDM0cHgsIDV2dywgNjRweCk7IGxldHRlci1zcGFjaW5nOiAtMC4wNTVlbTsgbGluZS1oZWlnaHQ6IDAuOTg7IG1hcmdpbjogMCAwIDIycHg7IH1cbiAgICAgICAgICAubW9jay1jb3B5IHAgeyBjb2xvcjogdmFyKC0tbXV0ZWQpOyBsaW5lLWhlaWdodDogMS41NTsgbWFyZ2luOiAwIDAgMjRweDsgbWF4LXdpZHRoOiA1MjBweDsgfVxuICAgICAgICAgIC5tb2NrLXZpc3VhbCB7XG4gICAgICAgICAgICBhbGlnbi1jb250ZW50OiBlbmQ7XG4gICAgICAgICAgICBiYWNrZ3JvdW5kOiB2YXIoLS1wcmltYXJ5KTtcbiAgICAgICAgICAgIGJvcmRlci1yYWRpdXM6IDIwcHg7XG4gICAgICAgICAgICBjb2xvcjogdmFyKC0tYmFja2dyb3VuZCk7XG4gICAgICAgICAgICBkaXNwbGF5OiBncmlkO1xuICAgICAgICAgICAgbWluLWhlaWdodDogMjUwcHg7XG4gICAgICAgICAgICBvdmVyZmxvdzogaGlkZGVuO1xuICAgICAgICAgICAgcGFkZGluZzogMjhweDtcbiAgICAgICAgICAgIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgLm1vY2stdmlzdWFsOjpiZWZvcmUge1xuICAgICAgICAgICAgYmFja2dyb3VuZDogdmFyKC0tYWNjZW50KTtcbiAgICAgICAgICAgIGJvcmRlci1yYWRpdXM6IDUwJTtcbiAgICAgICAgICAgIGNvbnRlbnQ6IFwiXCI7XG4gICAgICAgICAgICBoZWlnaHQ6IDIxMHB4O1xuICAgICAgICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgICAgICAgcmlnaHQ6IC03MHB4O1xuICAgICAgICAgICAgdG9wOiAtODBweDtcbiAgICAgICAgICAgIHdpZHRoOiAyMTBweDtcbiAgICAgICAgICB9XG4gICAgICAgICAgLm1vY2stdmlzdWFsIHN0cm9uZyB7IGZvbnQtc2l6ZTogMjJweDsgbGluZS1oZWlnaHQ6IDEuMTU7IG1heC13aWR0aDogMjIwcHg7IHBvc2l0aW9uOiByZWxhdGl2ZTsgfVxuICAgICAgICAgIC5waG9uZSB7IGJvcmRlcjogOHB4IHNvbGlkIHZhcigtLWZvcmVncm91bmQpOyBib3JkZXItcmFkaXVzOiAzNHB4OyB9XG4gICAgICAgICAgLnBob25lLXNjcmVlbiB7IG1pbi1oZWlnaHQ6IDU0MHB4OyB9XG4gICAgICAgICAgLnBob25lLW5hdiB7IHBhZGRpbmc6IDE4cHg7IH1cbiAgICAgICAgICAucGhvbmUtbmF2IGltZyB7IGhlaWdodDogMzBweDsgbWF4LXdpZHRoOiAxMTJweDsgb2JqZWN0LWZpdDogY29udGFpbjsgb2JqZWN0LXBvc2l0aW9uOiBsZWZ0IGNlbnRlcjsgfVxuICAgICAgICAgIC5waG9uZS1tYXJrIGltZyB7IGhlaWdodDogMzBweDsgd2lkdGg6IDMwcHg7IH1cbiAgICAgICAgICAuY29tcGFjdC13b3JkbWFyayB7IGZvbnQtd2VpZ2h0OiA4MDA7IH1cbiAgICAgICAgICAucGhvbmUtY29weSB7IHBhZGRpbmc6IDM0cHggMjJweCAyNHB4OyB9XG4gICAgICAgICAgLnBob25lLWNvcHkgaDMgeyBmb250LXNpemU6IDM4cHg7IGxldHRlci1zcGFjaW5nOiAtMC4wNWVtOyBsaW5lLWhlaWdodDogMC45ODsgbWFyZ2luOiAwIDAgMThweDsgfVxuICAgICAgICAgIC5waG9uZS1jb3B5IHAgeyBjb2xvcjogdmFyKC0tbXV0ZWQpOyBmb250LXNpemU6IDE0cHg7IGxpbmUtaGVpZ2h0OiAxLjU1OyBtYXJnaW46IDA7IH1cbiAgICAgICAgICAucGhvbmUtYWNjZW50IHsgYmFja2dyb3VuZDogdmFyKC0tYWNjZW50KTsgaGVpZ2h0OiAxMjBweDsgbWFyZ2luOiAxOHB4IDIycHggMjJweDsgfVxuICAgICAgICAgIC5zeXN0ZW0ge1xuICAgICAgICAgICAgZGlzcGxheTogZ3JpZDtcbiAgICAgICAgICAgIGdhcDogNDhweDtcbiAgICAgICAgICAgIGdyaWQtdGVtcGxhdGUtY29sdW1uczogMWZyIDFmcjtcbiAgICAgICAgICAgIHBhZGRpbmc6IDcycHggMDtcbiAgICAgICAgICB9XG4gICAgICAgICAgLnBhbGV0dGUgeyBkaXNwbGF5OiBncmlkOyBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IHJlcGVhdCg0LCAxZnIpOyBtaW4taGVpZ2h0OiAyNTBweDsgfVxuICAgICAgICAgIC5zd2F0Y2ggeyBhbGlnbi1pdGVtczogZW5kOyBkaXNwbGF5OiBmbGV4OyBwYWRkaW5nOiAxNnB4OyB9XG4gICAgICAgICAgLnN3YXRjaCBzcGFuIHsgYmFja2dyb3VuZDogdmFyKC0tYmFja2dyb3VuZCk7IGNvbG9yOiB2YXIoLS1mb3JlZ3JvdW5kKTsgZm9udC1mYW1pbHk6IHZhcigtLW1vbm8pOyBmb250LXNpemU6IDExcHg7IHBhZGRpbmc6IDVweCA3cHg7IH1cbiAgICAgICAgICAudHlwZS1zcGVjaW1lbiB7IGJvcmRlci10b3A6IDFweCBzb2xpZCB2YXIoLS1ib3JkZXIpOyBwYWRkaW5nLXRvcDogMjRweDsgfVxuICAgICAgICAgIC50eXBlLXNwZWNpbWVuIHN0cm9uZyB7IGRpc3BsYXk6IGJsb2NrOyBmb250LXNpemU6IGNsYW1wKDQ0cHgsIDZ2dywgNzZweCk7IGxldHRlci1zcGFjaW5nOiAtMC4wNWVtOyBsaW5lLWhlaWdodDogMC45NTsgfVxuICAgICAgICAgIC50eXBlLXNwZWNpbWVuIGNvZGUgeyBjb2xvcjogdmFyKC0tbXV0ZWQpOyBkaXNwbGF5OiBibG9jazsgZm9udC1mYW1pbHk6IHZhcigtLW1vbm8pOyBtYXJnaW4tdG9wOiAyMHB4OyB9XG4gICAgICAgICAgLnZvaWNlIHsgYm9yZGVyLXRvcDogMXB4IHNvbGlkIHZhcigtLWJvcmRlcik7IGRpc3BsYXk6IGdyaWQ7IGdhcDogMzJweDsgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiAxZnIgMWZyOyBwYWRkaW5nOiAzNnB4IDA7IH1cbiAgICAgICAgICAudm9pY2UgaDIgeyBmb250LXNpemU6IDMwcHg7IGxldHRlci1zcGFjaW5nOiAtMC4wMzVlbTsgbWFyZ2luOiAwOyB9XG4gICAgICAgICAgLnZvaWNlIGgzIHsgZm9udC1zaXplOiAxNHB4OyBtYXJnaW46IDAgMCAxMnB4OyB9XG4gICAgICAgICAgLnZvaWNlIHVsIHsgY29sb3I6IHZhcigtLW11dGVkKTsgbGluZS1oZWlnaHQ6IDEuNjsgbWFyZ2luOiAwOyBwYWRkaW5nLWxlZnQ6IDE4cHg7IH1cbiAgICAgICAgICAucmVmZXJlbmNlcyB7IGJvcmRlci10b3A6IDFweCBzb2xpZCB2YXIoLS1ib3JkZXIpOyBwYWRkaW5nLXRvcDogNTZweDsgfVxuICAgICAgICAgIC5yZWZlcmVuY2UtZ3JpZCB7IGRpc3BsYXk6IGdyaWQ7IGdhcDogMThweDsgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiByZXBlYXQoNCwgMWZyKTsgfVxuICAgICAgICAgIGZpZ3VyZSB7IG1hcmdpbjogMDsgfVxuICAgICAgICAgIGZpZ3VyZSBpbWcgeyBhc3BlY3QtcmF0aW86IDkgLyAxNjsgYmFja2dyb3VuZDogdmFyKC0tc3VyZmFjZSk7IGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWJvcmRlcik7IG9iamVjdC1maXQ6IGNvbnRhaW47IHdpZHRoOiAxMDAlOyB9XG4gICAgICAgICAgZmlnY2FwdGlvbiB7IGNvbG9yOiB2YXIoLS1tdXRlZCk7IGZvbnQtc2l6ZTogMTFweDsgbWFyZ2luLXRvcDogOHB4OyB9XG4gICAgICAgICAgQG1lZGlhIChtYXgtd2lkdGg6IDg2MHB4KSB7XG4gICAgICAgICAgICBtYWluIHsgcGFkZGluZzogMCAxOHB4IDQ4cHg7IH1cbiAgICAgICAgICAgIC5pZGVudGl0eSwgLnN5c3RlbSB7IGdyaWQtdGVtcGxhdGUtY29sdW1uczogMWZyOyB9XG4gICAgICAgICAgICAuaWRlbnRpdHkgeyBnYXA6IDI4cHg7IHBhZGRpbmc6IDUycHggMCA0NHB4OyB9XG4gICAgICAgICAgICAuc2hvd2Nhc2UgeyBib3JkZXItcmFkaXVzOiAyMHB4OyBwYWRkaW5nOiAyMHB4OyB9XG4gICAgICAgICAgICAuZGV2aWNlcyB7IGdyaWQtdGVtcGxhdGUtY29sdW1uczogMWZyOyB9XG4gICAgICAgICAgICAucGhvbmUgeyBtYXJnaW46IDAgYXV0bzsgbWF4LXdpZHRoOiAzMjBweDsgd2lkdGg6IDEwMCU7IH1cbiAgICAgICAgICAgIC5yZWZlcmVuY2UtZ3JpZCB7IGdyaWQtdGVtcGxhdGUtY29sdW1uczogcmVwZWF0KDIsIDFmcik7IH1cbiAgICAgICAgICB9XG4gICAgICAgICAgQG1lZGlhIChtYXgtd2lkdGg6IDU2MHB4KSB7XG4gICAgICAgICAgICAubWFzdGhlYWQgeyBtaW4taGVpZ2h0OiA3MnB4OyB9XG4gICAgICAgICAgICAuaWRlbnRpdHkgaDEgeyBmb250LXNpemU6IDUycHg7IH1cbiAgICAgICAgICAgIC5zZWN0aW9uLWhlYWRpbmcgeyBhbGlnbi1pdGVtczogc3RhcnQ7IGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47IH1cbiAgICAgICAgICAgIC5zZWN0aW9uLWhlYWRpbmcgcCB7IHRleHQtYWxpZ246IGxlZnQ7IH1cbiAgICAgICAgICAgIC5tb2NrLWhlcm8geyBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IDFmcjsgcGFkZGluZzogMjRweDsgfVxuICAgICAgICAgICAgLm1vY2stdmlzdWFsIHsgbWluLWhlaWdodDogMTkwcHg7IH1cbiAgICAgICAgICAgIC5uYXYtbGlua3MgeyBkaXNwbGF5OiBub25lOyB9XG4gICAgICAgICAgICAucGFsZXR0ZSB7IGdyaWQtdGVtcGxhdGUtY29sdW1uczogMWZyIDFmcjsgfVxuICAgICAgICAgICAgLnZvaWNlIHsgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiAxZnI7IH1cbiAgICAgICAgICB9XG4gICAgICAgICAgQG1lZGlhIChwcmVmZXJzLXJlZHVjZWQtbW90aW9uOiBuby1wcmVmZXJlbmNlKSB7XG4gICAgICAgICAgICAuYnV0dG9uIHsgdHJhbnNpdGlvbjogYmFja2dyb3VuZC1jb2xvciAxNjBtcyBlYXNlLCB0cmFuc2Zvcm0gMTYwbXMgZWFzZTsgfVxuICAgICAgICAgICAgLmJ1dHRvbjpob3ZlciB7IGJhY2tncm91bmQ6IHZhcigtLXByaW1hcnktaG92ZXIpOyB0cmFuc2Zvcm06IHRyYW5zbGF0ZVkoLTFweCk7IH1cbiAgICAgICAgICB9XG4gICAgICAgICAgLmJ1dHRvbjpmb2N1cy12aXNpYmxlIHsgb3V0bGluZTogM3B4IHNvbGlkIHZhcigtLWFjY2VudCk7IG91dGxpbmUtb2Zmc2V0OiAzcHg7IH1cbiAgICAgICAgPC9zdHlsZT5cbiAgICAgIDwvaGVhZD5cbiAgICAgIDxib2R5PlxuICAgICAgICA8bWFpbj5cbiAgICAgICAgICA8aGVhZGVyIGNsYXNzPVwibWFzdGhlYWRcIj5cbiAgICAgICAgICAgICR7aWRlbnRpdHlNYXJrfVxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJtZXRhXCI+QnJhbmQgc3lzdGVtIC8gJHtodG1sKGJyYW5kLnNjaGVtYVZlcnNpb24pfTwvc3Bhbj5cbiAgICAgICAgICA8L2hlYWRlcj5cblxuICAgICAgICAgIDxzZWN0aW9uIGNsYXNzPVwiaWRlbnRpdHlcIiBhcmlhLWxhYmVsbGVkYnk9XCJicmFuZC1uYW1lXCI+XG4gICAgICAgICAgICA8aDEgaWQ9XCJicmFuZC1uYW1lXCI+JHtodG1sKGJyYW5kLm5hbWUpfTwvaDE+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiaWRlbnRpdHktY29weVwiPlxuICAgICAgICAgICAgICA8cD4ke2h0bWwoYnJhbmQuZGVzY3JpcHRpb24pfTwvcD5cbiAgICAgICAgICAgICAgJHthY3Rpb259XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8L3NlY3Rpb24+XG5cbiAgICAgICAgICA8c2VjdGlvbiBjbGFzcz1cInNob3djYXNlXCIgYXJpYS1sYWJlbGxlZGJ5PVwibW9ja3VwLXRpdGxlXCI+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwic2VjdGlvbi1oZWFkaW5nXCI+XG4gICAgICAgICAgICAgIDxoMiBpZD1cIm1vY2t1cC10aXRsZVwiPldlYnNpdGUgbW9ja3VwczwvaDI+XG4gICAgICAgICAgICAgIDxwPk9uZSByZXNwb25zaXZlIGRpcmVjdGlvbiBzaG93biBhdCBkZXNrdG9wIGFuZCBtb2JpbGUgc2l6ZXMuPC9wPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZGV2aWNlc1wiPlxuICAgICAgICAgICAgICA8YXJ0aWNsZSBjbGFzcz1cImJyb3dzZXJcIiBhcmlhLWxhYmVsPVwiRGVza3RvcCB3ZWJzaXRlIG1vY2t1cFwiPlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJicm93c2VyLWJhclwiPjxpPjwvaT48aT48L2k+PGk+PC9pPjwvZGl2PlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJicm93c2VyLW5hdlwiPlxuICAgICAgICAgICAgICAgICAgJHtpZGVudGl0eU1hcmt9XG4gICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibmF2LWxpbmtzXCI+PHNwYW4+T3ZlcnZpZXc8L3NwYW4+PHNwYW4+UHJvZHVjdDwvc3Bhbj48c3Bhbj5SZXNvdXJjZXM8L3NwYW4+PC9kaXY+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm1vY2staGVyb1wiPlxuICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm1vY2stY29weVwiPlxuICAgICAgICAgICAgICAgICAgICA8aDM+JHtodG1sKGhlYWRsaW5lKX08L2gzPlxuICAgICAgICAgICAgICAgICAgICA8cD4ke2h0bWwoYnJhbmQuZGVzY3JpcHRpb24pfTwvcD5cbiAgICAgICAgICAgICAgICAgICAgJHthY3Rpb259XG4gICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJtb2NrLXZpc3VhbFwiPlxuICAgICAgICAgICAgICAgICAgICA8c3Ryb25nPiR7aHRtbChicmFuZC52b2ljZS5wcmluY2lwbGVzWzBdID8/IGJyYW5kLm5hbWUpfTwvc3Ryb25nPlxuICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgIDwvYXJ0aWNsZT5cblxuICAgICAgICAgICAgICA8YXJ0aWNsZSBjbGFzcz1cInBob25lXCIgYXJpYS1sYWJlbD1cIk1vYmlsZSB3ZWJzaXRlIG1vY2t1cFwiPlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJwaG9uZS1zY3JlZW5cIj5cbiAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJwaG9uZS1uYXZcIj5cbiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJwaG9uZS1tYXJrXCI+JHtjb21wYWN0TWFya308L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibWV0YVwiPk1lbnU8L3NwYW4+XG4gICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJwaG9uZS1jb3B5XCI+XG4gICAgICAgICAgICAgICAgICAgIDxoMz4ke2h0bWwoaGVhZGxpbmUpfTwvaDM+XG4gICAgICAgICAgICAgICAgICAgIDxwPiR7aHRtbChicmFuZC5kZXNjcmlwdGlvbil9PC9wPlxuICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwicGhvbmUtYWNjZW50XCI+PC9kaXY+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgIDwvYXJ0aWNsZT5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDwvc2VjdGlvbj5cblxuICAgICAgICAgIDxzZWN0aW9uIGNsYXNzPVwic3lzdGVtXCIgYXJpYS1sYWJlbD1cIkJyYW5kIHN5c3RlbVwiPlxuICAgICAgICAgICAgPGRpdj5cbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInNlY3Rpb24taGVhZGluZ1wiPjxoMj5Db3JlIHBhbGV0dGU8L2gyPjwvZGl2PlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwicGFsZXR0ZVwiPlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJzd2F0Y2hcIiBzdHlsZT1cImJhY2tncm91bmQ6IHZhcigtLXByaW1hcnkpXCI+PHNwYW4+JHticmFuZC5jb2xvcnMucHJpbWFyeX08L3NwYW4+PC9kaXY+XG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInN3YXRjaFwiIHN0eWxlPVwiYmFja2dyb3VuZDogdmFyKC0tYWNjZW50KVwiPjxzcGFuPiR7YnJhbmQuY29sb3JzLmFjY2VudH08L3NwYW4+PC9kaXY+XG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInN3YXRjaFwiIHN0eWxlPVwiYmFja2dyb3VuZDogdmFyKC0tc3VyZmFjZSlcIj48c3Bhbj4ke2JyYW5kLmNvbG9ycy5zdXJmYWNlfTwvc3Bhbj48L2Rpdj5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwic3dhdGNoXCIgc3R5bGU9XCJiYWNrZ3JvdW5kOiB2YXIoLS1mb3JlZ3JvdW5kKVwiPjxzcGFuPiR7YnJhbmQuY29sb3JzLmZvcmVncm91bmR9PC9zcGFuPjwvZGl2PlxuICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPGRpdj5cbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInNlY3Rpb24taGVhZGluZ1wiPjxoMj5UeXBvZ3JhcGh5PC9oMj48L2Rpdj5cbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInR5cGUtc3BlY2ltZW5cIj5cbiAgICAgICAgICAgICAgICA8c3Ryb25nPkFhIDAxPC9zdHJvbmc+XG4gICAgICAgICAgICAgICAgPGNvZGU+JHtodG1sKGJyYW5kLnR5cG9ncmFwaHkuc2Fucyl9PC9jb2RlPlxuICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDwvc2VjdGlvbj5cblxuICAgICAgICAgIDxzZWN0aW9uIGNsYXNzPVwidm9pY2VcIiBhcmlhLWxhYmVsbGVkYnk9XCJ2b2ljZS10aXRsZVwiPlxuICAgICAgICAgICAgPGRpdj5cbiAgICAgICAgICAgICAgPGgyIGlkPVwidm9pY2UtdGl0bGVcIj5Wb2ljZTwvaDI+XG4gICAgICAgICAgICAgIDxwIGNsYXNzPVwibWV0YVwiPiR7aHRtbChicmFuZC52b2ljZS50b25lLmpvaW4oXCIsIFwiKSl9PC9wPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8ZGl2PlxuICAgICAgICAgICAgICA8aDM+UHJpbmNpcGxlczwvaDM+XG4gICAgICAgICAgICAgIDx1bD4ke2xpc3RJdGVtcyhicmFuZC52b2ljZS5wcmluY2lwbGVzKX08L3VsPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPC9zZWN0aW9uPlxuXG4gICAgICAgICAgJHtnYWxsZXJ5fVxuICAgICAgICA8L21haW4+XG4gICAgICA8L2JvZHk+XG4gICAgPC9odG1sPlxuICAgIGBcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICk7XG59XG4iXX0=
@@ -1 +1 @@
1
- {"root":["../src/bootstrap.ts","../src/bun.ts","../src/cli.ts","../src/root.ts","../src/appkit/cli.ts","../src/appkit/env-export.ts","../src/appkit/index.ts","../src/auth/cli.ts","../src/auth/index.ts","../src/auth/options.ts","../src/brand/cli.ts","../src/brand/export.ts","../src/brand/index.ts","../src/brand/options.ts","../src/brand/run.ts","../src/genie-code/cli.ts","../src/genie-code/config.ts","../src/genie-code/index.ts","../src/genie-code/install.ts","../src/genie-code/options.ts","../src/genie-code/runner.ts","../src/genie-code/runtime.ts","../src/graphiti/cli.ts","../src/graphiti/index.ts","../src/graphiti/options.ts","../src/lakebase-proxy/cancellation.ts","../src/lakebase-proxy/cli.ts","../src/lakebase-proxy/index.ts","../src/lakebase-proxy/options.ts","../src/lakebase-proxy/protocol.ts","../src/lakebase-proxy/proxy.ts","../src/model-gateway/cli.ts","../src/model-gateway/index.ts","../src/model-gateway/server.ts","../src/tunnel/app.ts","../src/tunnel/cli.ts","../src/tunnel/index.ts","../src/tunnel/options.ts","../src/tunnel/proxy.ts","../index.ts","../bin/dbx-genie.ts","../bin/dbx-graphiti.ts","../bin/dbx-lakebase-proxy.ts","../bin/dbx-model-gateway.ts","../bin/dbx-tools.ts"],"version":"5.9.3"}
1
+ {"root":["../src/bootstrap.ts","../src/bun.ts","../src/cli.ts","../src/root.ts","../src/appkit/cli.ts","../src/appkit/env-export.ts","../src/appkit/index.ts","../src/auth/cli.ts","../src/auth/index.ts","../src/auth/options.ts","../src/brand/cli.ts","../src/brand/export.ts","../src/brand/index.ts","../src/brand/options.ts","../src/brand/run.ts","../src/brand/showcase.ts","../src/genie-code/cli.ts","../src/genie-code/config.ts","../src/genie-code/index.ts","../src/genie-code/install.ts","../src/genie-code/options.ts","../src/genie-code/runner.ts","../src/genie-code/runtime.ts","../src/graphiti/cli.ts","../src/graphiti/index.ts","../src/graphiti/options.ts","../src/lakebase-proxy/cancellation.ts","../src/lakebase-proxy/cli.ts","../src/lakebase-proxy/index.ts","../src/lakebase-proxy/options.ts","../src/lakebase-proxy/protocol.ts","../src/lakebase-proxy/proxy.ts","../src/model-gateway/cli.ts","../src/model-gateway/index.ts","../src/model-gateway/server.ts","../src/tunnel/app.ts","../src/tunnel/cli.ts","../src/tunnel/index.ts","../src/tunnel/options.ts","../src/tunnel/proxy.ts","../index.ts","../bin/dbx-genie.ts","../bin/dbx-graphiti.ts","../bin/dbx-lakebase-proxy.ts","../bin/dbx-model-gateway.ts","../bin/dbx-tools.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -34,27 +34,27 @@
34
34
  "dependencies": {
35
35
  "@clack/prompts": "^1.7.0",
36
36
  "@databricks/appkit": "0.81.0",
37
- "@dbx-tools/appkit": "^0.9.139",
38
- "@dbx-tools/appkit-generator": "^0.9.139",
39
- "@dbx-tools/appkit-memory": "^0.9.139",
40
- "@dbx-tools/appkit-model-gateway": "^0.9.139",
41
- "@dbx-tools/appkit-web-search": "^0.9.139",
42
- "@dbx-tools/auth": "^0.9.139",
43
- "@dbx-tools/auth-gate": "^0.9.139",
44
- "@dbx-tools/cli-args": "^0.9.139",
45
- "@dbx-tools/cli-service": "^0.9.139",
46
- "@dbx-tools/core": "^0.9.139",
47
- "@dbx-tools/databricks": "^0.9.139",
48
- "@dbx-tools/email": "^0.9.139",
49
- "@dbx-tools/lakebase": "^0.9.139",
50
- "@dbx-tools/postgres": "^0.9.139",
51
- "@dbx-tools/shared-auth": "^0.9.139",
52
- "@dbx-tools/shared-core": "^0.9.139",
53
- "@dbx-tools/shared-email": "^0.9.139",
54
- "@dbx-tools/shared-generator": "^0.9.139",
55
- "@dbx-tools/shared-genie-code": "^0.9.139",
56
- "@dbx-tools/shared-model-gateway": "^0.9.139",
57
- "@dbx-tools/tunnel": "^0.9.139",
37
+ "@dbx-tools/appkit": "^0.9.141",
38
+ "@dbx-tools/appkit-generator": "^0.9.141",
39
+ "@dbx-tools/appkit-memory": "^0.9.141",
40
+ "@dbx-tools/appkit-model-gateway": "^0.9.141",
41
+ "@dbx-tools/appkit-web-search": "^0.9.141",
42
+ "@dbx-tools/auth": "^0.9.141",
43
+ "@dbx-tools/auth-gate": "^0.9.141",
44
+ "@dbx-tools/cli-args": "^0.9.141",
45
+ "@dbx-tools/cli-service": "^0.9.141",
46
+ "@dbx-tools/core": "^0.9.141",
47
+ "@dbx-tools/databricks": "^0.9.141",
48
+ "@dbx-tools/email": "^0.9.141",
49
+ "@dbx-tools/lakebase": "^0.9.141",
50
+ "@dbx-tools/postgres": "^0.9.141",
51
+ "@dbx-tools/shared-auth": "^0.9.141",
52
+ "@dbx-tools/shared-core": "^0.9.141",
53
+ "@dbx-tools/shared-email": "^0.9.141",
54
+ "@dbx-tools/shared-generator": "^0.9.141",
55
+ "@dbx-tools/shared-genie-code": "^0.9.141",
56
+ "@dbx-tools/shared-model-gateway": "^0.9.141",
57
+ "@dbx-tools/tunnel": "^0.9.141",
58
58
  "commander": "^15.0.0",
59
59
  "concurrently": "^10.0.3",
60
60
  "get-port": "^7.1.0",
@@ -178,7 +178,7 @@
178
178
  "./package.json": "./package.json"
179
179
  }
180
180
  },
181
- "version": "0.9.139",
181
+ "version": "0.9.141",
182
182
  "types": "./lib/index.d.ts",
183
183
  "dbxToolsConfig": {
184
184
  "tags": [
package/src/brand/cli.ts CHANGED
@@ -39,12 +39,14 @@ export function buildProgram(name = "dbx brand", dependencies: BrandCliDependenc
39
39
  const options = parseArgs(program, BrandExportOptionsSchema);
40
40
  const result = await runExport(prompt, options, {
41
41
  onProgress(event) {
42
- writeProgress(`[${event.stage}] ${event.message}`);
42
+ writeProgress(event.message);
43
43
  },
44
44
  });
45
45
  writeResult({
46
+ root: result.outputDirectory,
46
47
  brand: result.brandYamlPath,
47
48
  manifest: result.manifestPath,
49
+ ...(result.showcasePath ? { showcase: result.showcasePath } : {}),
48
50
  references: result.referenceCount,
49
51
  });
50
52
  });
@@ -10,6 +10,7 @@ import type { BrandResearch } from "@dbx-tools/appkit-generator";
10
10
  import type { BrandPackage } from "@dbx-tools/shared-generator";
11
11
  import { BrandPackageSchema } from "@dbx-tools/shared-generator";
12
12
  import { stringify } from "yaml";
13
+ import { renderBrandShowcase } from "./showcase.ts";
13
14
 
14
15
  /** Inputs required to write a generated brand package. */
15
16
  export interface WriteBrandPackageOptions {
@@ -17,6 +18,7 @@ export interface WriteBrandPackageOptions {
17
18
  readonly prompt: string;
18
19
  readonly brandPackage: BrandPackage;
19
20
  readonly research: BrandResearch;
21
+ readonly showcase?: boolean;
20
22
  readonly force?: boolean;
21
23
  }
22
24
 
@@ -25,6 +27,7 @@ export interface WrittenBrandPackage {
25
27
  readonly outputDirectory: string;
26
28
  readonly brandYamlPath: string;
27
29
  readonly manifestPath: string;
30
+ readonly showcasePath?: string;
28
31
  readonly referenceCount: number;
29
32
  }
30
33
 
@@ -86,12 +89,14 @@ export async function writeBrandPackage(
86
89
  ): Promise<WrittenBrandPackage> {
87
90
  const outputDirectory = path.resolve(options.outputDirectory);
88
91
  const brandYamlPath = path.join(outputDirectory, "brand.yaml");
92
+ const showcasePath = path.join(outputDirectory, "showcase.html");
89
93
  const packageExists = await fileExists(brandYamlPath);
90
94
  if (packageExists && !options.force) {
91
95
  throw new Error(`Brand package already exists at ${brandYamlPath}; pass --force to replace it`);
92
96
  }
93
97
  if (options.force) {
94
98
  await rm(path.join(outputDirectory, "references"), { recursive: true, force: true });
99
+ await rm(showcasePath, { force: true });
95
100
  }
96
101
 
97
102
  const visualReferences = structuredClone(options.brandPackage.visualReferences);
@@ -134,11 +139,15 @@ export async function writeBrandPackage(
134
139
  2,
135
140
  )}\n`,
136
141
  );
142
+ if (options.showcase) {
143
+ await writeAtomic(showcasePath, renderBrandShowcase(brandPackage));
144
+ }
137
145
 
138
146
  return {
139
147
  outputDirectory,
140
148
  brandYamlPath,
141
149
  manifestPath,
150
+ ...(options.showcase ? { showcasePath } : {}),
142
151
  referenceCount,
143
152
  };
144
153
  }
@@ -6,6 +6,7 @@ export * as cli from "./cli.ts";
6
6
  export * as exportModule from "./export.ts";
7
7
  export * as options from "./options.ts";
8
8
  export * as run from "./run.ts";
9
+ export * as showcase from "./showcase.ts";
9
10
  export type { BrandCliDependencies } from "./cli.ts";
10
11
  export type { WriteBrandPackageOptions, WrittenBrandPackage } from "./export.ts";
11
12
  export { BrandExportOptionsSchema } from "./options.ts";
@@ -17,8 +17,10 @@ export const BrandExportOptionsSchema = z
17
17
  .string()
18
18
  .trim()
19
19
  .min(1)
20
- .default("branding")
21
- .describe("Output directory for brand.yaml, provenance, and visual references."),
20
+ .optional()
21
+ .describe(
22
+ "Persistent output directory for brand.yaml, showcase, provenance, and visual references. Omit for a temporary directory.",
23
+ ),
22
24
  model: z
23
25
  .string()
24
26
  .trim()
@@ -39,6 +41,10 @@ export const BrandExportOptionsSchema = z
39
41
  .max(20_000)
40
42
  .default(8_000)
41
43
  .describe("Maximum characters retained from each source page."),
44
+ showcase: z
45
+ .boolean()
46
+ .default(true)
47
+ .describe("Responsive HTML brand presentation and website mockups."),
42
48
  force: z.boolean().default(false).describe("Replace files in an existing output directory."),
43
49
  })
44
50
  .strict()
package/src/brand/run.ts CHANGED
@@ -4,6 +4,9 @@
4
4
  * @module
5
5
  */
6
6
 
7
+ import { mkdtemp } from "node:fs/promises";
8
+ import { tmpdir } from "node:os";
9
+ import path from "node:path";
7
10
  import type { BrandResearch } from "@dbx-tools/appkit-generator";
8
11
  import { generateBrandPackage } from "@dbx-tools/appkit-generator/generate";
9
12
  import {
@@ -14,6 +17,7 @@ import {
14
17
  import { createWebSearchRuntime } from "@dbx-tools/appkit-web-search/runtime";
15
18
  import { runWebSearch } from "@dbx-tools/appkit-web-search/search";
16
19
  import { createWorkspaceClient } from "@dbx-tools/databricks/workspace-client";
20
+ import { object } from "@dbx-tools/shared-core";
17
21
  import type { BrandGenerationProgressEvent } from "@dbx-tools/shared-generator";
18
22
 
19
23
  import { writeBrandPackage, type WrittenBrandPackage } from "./export.ts";
@@ -59,6 +63,21 @@ export async function exportBrandFromPrompt(
59
63
  ...(options.profile ? { cacheIdentity: `profile:${options.profile}` } : {}),
60
64
  },
61
65
  signal,
66
+ async (event) => {
67
+ if (!object.isRecord(event)) return;
68
+ const status = typeof event.status === "string" ? event.status : undefined;
69
+ const eventMessage =
70
+ typeof event.message === "string" ? event.message.trim() : undefined;
71
+ const detail = typeof event.detail === "string" ? event.detail.trim() : undefined;
72
+ await callbacks.onProgress?.({
73
+ type: "progress",
74
+ stage: "search",
75
+ message:
76
+ status === "search" && eventMessage
77
+ ? `Web search: ${eventMessage}`
78
+ : `Web search complete${detail ? `: ${detail}` : ""}`,
79
+ });
80
+ },
62
81
  ),
63
82
  fetch: (request, signal) => runWebFetch(request, runtime, signal),
64
83
  fetchAssetPage: (request, signal) => runWebAssetPageFetch(request, runtime, signal),
@@ -75,11 +94,14 @@ export async function exportBrandFromPrompt(
75
94
  },
76
95
  );
77
96
  if (!research) throw new Error("Brand generation completed without research provenance");
97
+ const outputDirectory =
98
+ options.output ?? (await mkdtemp(path.join(tmpdir(), "dbx-tools-brand-")));
78
99
  return writeBrandPackage({
79
- outputDirectory: options.output,
100
+ outputDirectory,
80
101
  prompt,
81
102
  brandPackage,
82
103
  research,
104
+ showcase: options.showcase,
83
105
  force: options.force,
84
106
  });
85
107
  }